据我所知,在ES7/ES2016中,在代码中放置多个await将类似于用promise链接.then(),这意味着它们将一个接一个地执行,而不是并行执行。例如,我们有以下代码:
await someCall();
await anotherCall();
我是否正确理解只有当someCall()完成时才会调用另一个otherCall()?并行调用它们最优雅的方式是什么?
我想在Node中使用它,所以也许有一个异步库的解决方案?
编辑:我对这个问题中提供的解决方案不满意:由于异步生成器中的非并行等待承诺,所以速度变慢,因为它使用生成器,我问的是一个更通用的用例。
我投票支持:
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]
}
我投票支持:
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并不是严格地并行运行它们,但它们不会阻碍任何执行,您可以通过捕获捕获上下文错误。
您可以调用多个异步函数,而无需等待它们。这将并行执行它们。执行此操作时,将返回的Promise保存在变量中,并在某个时刻单独或使用Promise.all()等待它们,然后处理结果。
还可以使用try…包装函数调用。。。catch处理单个异步操作的失败并提供回退逻辑。
下面是一个示例:观察日志,在单个异步函数开始执行时打印的日志会立即打印,即使第一个函数需要5秒才能解析。
函数someLongFunc(){return new Promise((resolve,reject)=>{console.log('执行功能1')setTimeout(分辨率,5000)})}函数另一个LongFunc(){return new Promise((resolve,reject)=>{console.log('执行功能2')setTimeout(分辨率,5000)})}异步函数main(){让某个LongFuncPromise,另一个LongFunc Promiseconst start=Date.now()尝试{someLongFuncPromise=someLongFunc()}捕获(ex){console.error('功能1期间出现问题')}尝试{anotherLongFuncPromise=另一个LongFunc()}捕获(ex){console.error('功能2期间出现问题')}等待某人LongFuncPromise等待另一个LongFuncPromiseconst totalTime=Date.now()-开始console.log('执行完成时间',totalTime)}main()
TL;博士
对并行函数调用使用Promise.all,当发生错误时,应答行为不正确。
首先,立即执行所有异步调用并获取所有Promise对象。其次,对Promise对象使用await。这样,当您等待第一个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安慰
注意:只要第一个等待调用发生在所有异步调用之后,那么等待调用是在同一条线上还是在不同的线上并不重要。见JohnnyHK的评论。
更新:根据@bergi的回答,这个答案在错误处理中的时间不同,它不会在错误发生时抛出错误,而是在所有承诺都执行后抛出错误。我将结果与@jonny的提示进行比较:[result1,result2]=Promise.all([async1(),async2()]),检查以下代码片段
常量correctAsync500ms=()=>{return new Promise(解析=>{setTimeout(resolve,500,'correct500msResult');});};常量correctAsync100ms=()=>{return new Promise(解析=>{setTimeout(resolve,100,'correct100msResult');});};常量拒绝同步100ms=()=>{return new Promise((resolve,reject)=>{setTimeout(拒绝,100,'拒绝100毫秒错误');});};const asyncInArray=异步(fun1,fun2)=>{const label='测试数组中的异步函数';尝试{console.time(标签);常量p1=fun1();常量p2=fun2();const result=[await p1,wait p2];console.timeEnd(标签);}捕获(e){console.error('错误为',e);console.timeEnd(标签);}};常量asyncInPromiseAll=async(fun1,fun2)=>{const label='使用Promise.all测试异步函数';尝试{console.time(标签);let[value1,value2]=等待Promise。all([fun1(),fun2()]);console.timeEnd(标签);}捕获(e){console.error('错误为',e);console.timeEnd(标签);}};(异步()=>{console.group('无错误的同步功能');console.log('无错误的同步函数:启动')等待异步InArray(correctAsync500ms,correctAsync 100ms);等待异步InPromiseAll(correctAsync500ms,correctAsync 100ms);console.groupEnd();console.group('带错误的同步功能');console.log('带有错误的同步函数:start')等待异步InArray(correctAsync500ms,rejectAsync100ms);等待异步InPromiseAll(correctAsync500ms,rejectAsync100ms);console.groupEnd();})();
您可以等待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,同时它将提供哪些操作失败和哪些操作未失败的确切细节。