据我了解,在ES7 / ES2016中,在代码中放置多个await
将类似于使用promises链接.then()
,这意味着它们将一个接一个地执行而不是在parallerl中执行。所以,例如,我们有这个代码:
await someCall();
await anotherCall();
我是否正确理解anotherCall()
仅在someCall()
完成后才会被调用?什么是最优雅的方式同时调用它们?
我想在Node中使用它,所以也许有一个带异步库的解决方案?
编辑:我对这个问题提供的解决方案不满意:Slowdown due to non-parallel awaiting of promises in async generators,因为它使用了生成器,而且我在询问更常见的用例。答案 0 :(得分:437)
你可以等待Promise.all():
await Promise.all([someCall(), anotherCall()]);
答案 1 :(得分:86)
<强> TL; DR 强>
对并行函数调用使用Promise.all
,错误发生时答案行为不正确。
首先,立即执行 all 异步调用并获取所有Promise
个对象。其次,在await
对象上使用Promise
。这样,当您等待第一个Promise
解析其他异步调用时,仍然在进行中。总的来说,只有最慢的异步调用才会等待。例如:
// Begin first call and store promise without waiting
const someResult = someCall();
// Begin second call and store promise without waiting
const anotherResult = anotherCall();
// Now we await for both results, whose async processes have already been started
const finalResult = [await someResult, await anotherResult];
// At this point all calls have been resolved
// Now when accessing someResult| anotherResult,
// you will have a value instead of a promise
JSbin示例:http://jsbin.com/xerifanima/edit?js,console
警告:只要第一次await
来电发生,await
来电就在同一行或同一行上并不重要在所有异步调用之后。见JohnnyHK的评论。
更新:根据@bergi's answer,此答案在错误处理方面有不同的时间, NOT 会在发生错误时抛出错误但在之后所有的承诺都被执行了。
我将结果与@ jonny的提示进行比较:[result1, result2] = Promise.all([async1(), async2()])
,请检查以下代码段
const correctAsync500ms = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, 'correct500msResult');
});
};
const correctAsync100ms = () => {
return new Promise(resolve => {
setTimeout(resolve, 100, 'correct100msResult');
});
};
const rejectAsync100ms = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, 'reject100msError');
});
};
const asyncInArray = async (fun1, fun2) => {
const label = 'test async functions in array';
try {
console.time(label);
const p1 = fun1();
const p2 = fun2();
const result = [await p1, await p2];
console.timeEnd(label);
} catch (e) {
console.error('error is', e);
console.timeEnd(label);
}
};
const asyncInPromiseAll = async (fun1, fun2) => {
const label = 'test async functions with Promise.all';
try {
console.time(label);
let [value1, value2] = await Promise.all([fun1(), fun2()]);
console.timeEnd(label);
} catch (e) {
console.error('error is', e);
console.timeEnd(label);
}
};
(async () => {
console.group('async functions without error');
console.log('async functions without error: start')
await asyncInArray(correctAsync500ms, correctAsync100ms);
await asyncInPromiseAll(correctAsync500ms, correctAsync100ms);
console.groupEnd();
console.group('async functions with error');
console.log('async functions with error: start')
await asyncInArray(correctAsync500ms, rejectAsync100ms);
await asyncInPromiseAll(correctAsync500ms, rejectAsync100ms);
console.groupEnd();
})();
答案 2 :(得分:69)
<强>更新强>
原始答案使正确处理承诺拒绝变得困难(在某些情况下是不可能的)。正确的解决方案是使用Promise.all
:
const [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
原始回答:
只需确保在等待其中任何一个之前调用这两个函数:
// Call both functions
const somePromise = someCall();
const anotherPromise = anotherCall();
// Await both promises
const someResult = await somePromise;
const anotherResult = await anotherPromise;
答案 3 :(得分:6)
我创建了a gist,用于测试解决诺言和结果的几种不同方法。看到有效的选项可能会有所帮助。
答案 4 :(得分:6)
还有一种没有Promise.all()并行执行的方法:
首先,我们有2个打印数字的功能:
function printNumber1() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log("Number1 is done");
resolve(10);
},1000);
});
}
function printNumber2() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log("Number2 is done");
resolve(20);
},500);
});
}
这是顺序的:
async function oneByOne() {
const number1 = await printNumber1();
const number2 = await printNumber2();
}
//Output: Number1 is done, Number2 is done
这是平行的:
async function inParallel() {
const promise1 = printNumber1();
const promise2 = printNumber2();
const number1 = await promise1;
const number2 = await promise2;
}
//Output: Number2 is done, Number1 is done
答案 5 :(得分:3)
function wait(ms, data) {
return new Promise( resolve => setTimeout(resolve, ms, data) );
}
(async function parallel() {
// step 1 - initiate all promises
let task1 = wait(2000, 'parallelTask1').catch(e=>e);
let task2 = wait(500, 'parallelTask2').catch(e=>e);
// step 2 - await all promises
task1 = await task1
task2 = await task2
// step 3 - all results are 100% ready
console.log(task1, task2)
})()
使用ES6,您甚至可以在步骤2中完成此操作
[task1, task2] = [await task1, await task2]
let five = getAsyncFive()
let ten = getAsyncTen()
let result = await five * await ten
.catch(e => e)
捕获错误并将其传递到链下,从而使承诺能够解决,而不是拒绝。
const wait = (ms, data) => new Promise( resolve => setTimeout(resolve, ms, data) )
const reject = (ms, data) => new Promise( (r, reject) => setTimeout(reject, ms, data) )
const e = e => 'err:' + e
const l = l => (console.log(l), l)
;(async function parallel() {
let task1 = reject(500, 'parallelTask1').catch(e).then(l)
let task2 = wait(2000, 'parallelTask2').catch(e).then(l)
let task3 = reject(1000, 'parallelTask3').catch(e).then(l)
console.log('WAITING')
task1 = await task1
task2 = await task2
task3 = await task3
console.log('FINISHED', task1, task2, task3)
})()
答案 6 :(得分:3)
等待Promise.all([someCall(),anotherCall()]);正如已经提到的那样,它将充当线程屏障(在CUDA中,在并行代码中非常常见),因此它将允许其中的所有诺言都能彼此不阻塞地运行,但将阻止执行继续,直到所有问题都解决为止。
值得分享的另一种方法是Node.js异步,如果您将任务直接链接到使用有限资源作为API调用,I /,通常还可以方便地控制并发量。 O操作等
// create a queue object with concurrency 2
var q = async.queue(function(task, callback) {
console.log('Hello ' + task.name);
callback();
}, 2);
// assign a callback
q.drain = function() {
console.log('All items have been processed');
};
// add some items to the queue
q.push({name: 'foo'}, function(err) {
console.log('Finished processing foo');
});
q.push({name: 'bar'}, function (err) {
console.log('Finished processing bar');
});
// add some items to the queue (batch-wise)
q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function(err) {
console.log('Finished processing item');
});
// add some items to the front of the queue
q.unshift({name: 'bar'}, function (err) {
console.log('Finished processing bar');
});
中级文章指导者(read more)的信用额
答案 7 :(得分:2)
这可以通过Promise.allSettled()来完成,它与Promise.all()
类似,但没有快速失败行为。
async function failure() {
throw "Failure!";
}
async function success() {
return "Success!";
}
const [failureResult, successResult] = await Promise.allSettled([failure(), success()]);
console.log(failureResult); // {status: "rejected", reason: "Failure!"}
console.log(successResult); // {status: "fulfilled", value: "Success!"}
注意:这是尖端功能,对浏览器的支持有限,因此我强烈建议为此功能包括一个polyfill。
答案 8 :(得分:1)
// A generic test function that can be configured
// with an arbitrary delay and to either resolve or reject
const test = (delay, resolveSuccessfully) => new Promise((resolve, reject) => setTimeout(() => {
console.log(`Done ${ delay }`);
resolveSuccessfully ? resolve(`Resolved ${ delay }`) : reject(`Reject ${ delay }`)
}, delay));
// Our async handler function
const handler = async () => {
// Promise 1 runs first, but resolves last
const p1 = test(10000, true);
// Promise 2 run second, and also resolves
const p2 = test(5000, true);
// Promise 3 runs last, but completes first (with a rejection)
// Note the catch to trap the error immediately
const p3 = test(1000, false).catch(e => console.log(e));
// Await all in parallel
const r = await Promise.all([p1, p2, p3]);
// Display the results
console.log(r);
};
// Run the handler
handler();
/*
Done 1000
Reject 1000
Done 5000
Done 10000
*/
虽然设置p1,p2和p3并非严格并行运行它们,但它们不会阻止任何执行,并且您可以通过捕获来捕获上下文错误。
答案 9 :(得分:0)
就我而言,我有几个要并行执行的任务,但是我需要对这些任务的结果做一些不同的事情。
function wait(ms, data) {
console.log('Starting task:', data, ms);
return new Promise(resolve => setTimeout(resolve, ms, data));
}
var tasks = [
async () => {
var result = await wait(1000, 'moose');
// do something with result
console.log(result);
},
async () => {
var result = await wait(500, 'taco');
// do something with result
console.log(result);
},
async () => {
var result = await wait(5000, 'burp');
// do something with result
console.log(result);
}
]
await Promise.all(tasks.map(p => p()));
console.log('done');
输出:
Starting task: moose 1000
Starting task: taco 500
Starting task: burp 5000
taco
moose
burp
done
答案 10 :(得分:0)
以下是有史以来最漂亮的前端 API 网关代码,它并行调用三个不同的服务,然后使用结果之一循环调用另一个服务 ProblemService
。
请注意,我使用 await
、async
和 Promise.all
的方式击败了整个 StackOverflow。
class ExamScoreboardService {
getExamScoreboard(examId) {
return Promise.all([
examService.getExaminees(examId),
examService.getExamOverview(examId),
examService.getExamScores(examId),
])
.then(async ([examinees, exam, examScores]) => {
const out = {}
await Promise.all(exam.questions.map(async q =>
problemService.getProblemById(q.problemId).then(problem => out[q.problemId] = problem.testcases.length)))
return [examinees, exam, examScores, out]
})
.then(values => {
const [examinees, exam, examScores, totalTestcasesOf] = values;
return new ExamScoreboard({examinees, exam, examScores, totalTestcasesOf});
})
}
}
答案 11 :(得分:-5)
我投票支持:
await Promise.all([someCall(), anotherCall()]);
请注意您调用函数的那一刻,它可能会导致意外结果:
// Supposing anotherCall() will trigger a request to create a new User
if (callFirst) {
await someCall();
} else {
await Promise.all([someCall(), anotherCall()]); // --> create new User here
}
但以下始终会触发创建新用户的请求
// Supposing anotherCall() will trigger a request to create a new User
const someResult = someCall();
const anotherResult = anotherCall(); // ->> This always creates new User
if (callFirst) {
await someCall();
} else {
const finalResult = [await someResult, await anotherResult]
}
答案 12 :(得分:-7)
我创建了一个辅助函数waitAll,可能它可以让它更甜。 它目前仅适用于 nodejs ,浏览器chrome中不。
//const parallel = async (...items) => {
const waitAll = async (...items) => {
//this function does start execution the functions
//the execution has been started before running this code here
//instead it collects of the result of execution of the functions
const temp = [];
for (const item of items) {
//this is not
//temp.push(await item())
//it does wait for the result in series (not in parallel), but
//it doesn't affect the parallel execution of those functions
//because they haven started earlier
temp.push(await item);
}
return temp;
};
//the async functions are executed in parallel before passed
//in the waitAll function
//const finalResult = await waitAll(someResult(), anotherResult());
//const finalResult = await parallel(someResult(), anotherResult());
//or
const [result1, result2] = await waitAll(someResult(), anotherResult());
//const [result1, result2] = await parallel(someResult(), anotherResult());