据我所知,在ES7/ES2016中,在代码中放置多个await将类似于用promise链接.then(),这意味着它们将一个接一个地执行,而不是并行执行。例如,我们有以下代码:
await someCall();
await anotherCall();
我是否正确理解只有当someCall()完成时才会调用另一个otherCall()?并行调用它们最优雅的方式是什么?
我想在Node中使用它,所以也许有一个异步库的解决方案?
编辑:我对这个问题中提供的解决方案不满意:由于异步生成器中的非并行等待承诺,所以速度变慢,因为它使用生成器,我问的是一个更通用的用例。
您可以等待Promise.all():
await Promise.all([someCall(), anotherCall()]);
要存储结果:
let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
请注意,Promise.all很快就会失败,这意味着一旦提供给它的一个承诺被拒绝,那么整个承诺就会被拒绝。
const happy=(v,ms)=>new Promise((resolve)=>setTimeout(()=>resolve(v),ms))const sad=(v,ms)=>new Promise((_,reject)=>setTimeout(()=>reject(v),ms))承诺。所有([快乐('快乐',100),悲伤('悲伤',50)]).then(console.log).catch(console.log)//“sad”
相反,如果您希望等待所有承诺兑现或拒绝,则可以使用Promise.allSettled。请注意,Internet Explorer本机不支持此方法。
const happy=(v,ms)=>new Promise((resolve)=>setTimeout(()=>resolve(v),ms))const sad=(v,ms)=>new Promise((_,reject)=>setTimeout(()=>reject(v),ms))承诺。一切都已解决([快乐('快乐',100),悲伤('悲伤',50)]).then(console.log)//[{“status”:“已完成”,“value”:“快乐”},{“status”:“拒绝”,“reason”:“悲伤”}]
注意:如果您使用Promise.在拒绝发生之前完成的所有操作都不会回滚,因此您可能需要注意这种情况。例如如果你有5个动作,4个快速,1个慢速和慢速拒绝。这4个操作可能已经执行,因此您可能需要回滚。在这种情况下,考虑使用Promise.allSettled,同时它将提供哪些操作失败和哪些操作未失败的确切细节。
我创建了一个助手函数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());
await 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');
});
Medium文章自动收录(阅读更多)
更新:
最初的答案使正确处理拒绝承诺变得困难(在某些情况下是不可能的)。正确的解决方案是使用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;
我投票支持:
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]
}
// 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并不是严格地并行运行它们,但它们不会阻碍任何执行,您可以通过捕获捕获上下文错误。