Javascript await Promise.all() 和 multiple await 有什么区别?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/45285129/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Any difference between await Promise.all() and multiple await?
提问by Hidden
Is there any difference between:
之间有什么区别:
const [result1, result2] = await Promise.all([task1(), task2()]);
and
和
const t1 = task1();
const t2 = task2();
const result1 = await t1;
const result2 = await t2;
and
和
const [t1, t2] = [task1(), task2()];
const [result1, result2] = [await t1, await t2];
回答by zzzzBov
Note:
This answer just covers the timing differences between
awaitin series andPromise.all. Be sure to read @mikep's comprehensive answer that also covers the more important differences in error handling.
注意:
该答案仅涵盖
await串联和Promise.all. 请务必阅读@mikep 的综合答案,其中还涵盖了错误处理中更重要的差异。
For the purposes of this answer I will be using some example methods:
出于此答案的目的,我将使用一些示例方法:
res(ms)is a function that takes an integer of milliseconds and returns a promise that resolves after that many milliseconds.rej(ms)is a function that takes an integer of milliseconds and returns a promise that rejects after that many milliseconds.
res(ms)是一个函数,它接受一个整数毫秒并返回一个承诺,该承诺在许多毫秒后解析。rej(ms)是一个函数,它接受一个整数毫秒并返回一个承诺,该承诺在这么多毫秒后拒绝。
Calling resstarts the timer. Using Promise.allto wait for a handful of delays will resolve after all the delays have finished, but remember they execute at the same time:
呼叫res启动计时器。使用Promise.all等待一些延迟将在所有延迟完成后解决,但请记住它们同时执行:
const data = await Promise.all([res(3000), res(2000), res(1000)])
// ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^
// delay 1 delay 2 delay 3
//
// ms ------1---------2---------3
// =============================O delay 1
// ===================O delay 2
// =========O delay 3
//
// =============================O Promise.all
async function example() {
const start = Date.now()
let i = 0
function res(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
console.log(`res #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
const data = await Promise.all([res(3000), res(2000), res(1000)])
console.log(`Promise.all finished`, Date.now() - start)
}
example()
This means that Promise.allwill resolve with the data from the inner promises after 3 seconds.
这意味着Promise.all将在 3 秒后使用来自内部承诺的数据进行解析。
But, Promise.allhas a "fail fast" behavior:
const data = await Promise.all([res(3000), res(2000), rej(1000)])
// ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^
// delay 1 delay 2 delay 3
//
// ms ------1---------2---------3
// =============================O delay 1
// ===================O delay 2
// =========X delay 3
//
// =========X Promise.all
async function example() {
const start = Date.now()
let i = 0
function res(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
console.log(`res #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
function rej(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
reject()
console.log(`rej #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
try {
const data = await Promise.all([res(3000), res(2000), rej(1000)])
} catch (error) {
console.log(`Promise.all finished`, Date.now() - start)
}
}
example()
If you use async-awaitinstead, you will have to wait for each promise to resolve sequentially, which may not be as efficient:
如果async-await改为使用,则必须等待每个 promise 依次解析,这可能效率不高:
const delay1 = res(3000)
const delay2 = res(2000)
const delay3 = rej(1000)
const data1 = await delay1
const data2 = await delay2
const data3 = await delay3
// ms ------1---------2---------3
// =============================O delay 1
// ===================O delay 2
// =========X delay 3
//
// =============================X await
async function example() {
const start = Date.now()
let i = 0
function res(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
console.log(`res #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
function rej(n) {
const id = ++i
return new Promise((resolve, reject) => {
setTimeout(() => {
reject()
console.log(`rej #${id} called after ${n} milliseconds`, Date.now() - start)
}, n)
})
}
try {
const delay1 = res(3000)
const delay2 = res(2000)
const delay3 = rej(1000)
const data1 = await delay1
const data2 = await delay2
const data3 = await delay3
} catch (error) {
console.log(`await finished`, Date.now() - start)
}
}
example()
回答by mikep
First difference - fail fast
第一个区别 - 快速失败
I agree with @zzzzBov's answer but "fail fast" advantage of Promise.all is not only the one difference. Some users in comments asks why to use Promise.all when it is only faster in negative scenario (when some task fails). And I ask why not? If I have two independent async parallel tasks and first one is resolved in very long time but second is rejected in very short time why to leave user wait for error mesage "very long time" instead of "very short time"? In real-life applications we must consider negative scenario. But OK - in this first difference you can decide which alternative to use Promise.all vs. multiple await.
我同意@zzzzBov 的回答,但 Promise.all 的“快速失败”优势不仅仅是一个区别。评论中的一些用户询问为什么使用 Promise.all 仅在负面情况下更快(当某些任务失败时)。我问为什么不呢?如果我有两个独立的异步并行任务,第一个在很长时间内解决,但第二个在很短的时间内被拒绝,为什么让用户等待错误消息“很长时间”而不是“很短时间”?在实际应用中,我们必须考虑负面情况。但是好的 - 在这第一个区别中,您可以决定使用 Promise.all 还是多个 await 的替代方案。
Second difference - error handling
第二个区别 - 错误处理
But when considering error handling YOU MUST use Promise.all. It is not possible to correctly handle errors of async parallel tasks triggered with multiple await. In negative scenario you will always end with UnhandledPromiseRejectionWarningand PromiseRejectionHandledWarningalthough you use try/catch anywhere. That is why Promise.all was designed. Of course someone could say that we can suppress that errors using process.on('unhandledRejection', err => {})and process.on('rejectionHandled', err => {})but it is not good practice. I found many examples on the internet that does not consider error handling for two or more independent async parallel tasks at all or consider it but in wrong way - just using try/catch and hoping it will catch errors. It's almost impossible to find good practice. That is why I'm writing this answer.
但是在考虑错误处理时,您必须使用 Promise.all。无法正确处理由多个 await 触发的异步并行任务的错误。在消极的情况下UnhandledPromiseRejectionWarning,PromiseRejectionHandledWarning尽管您在任何地方都使用 try/catch ,但您将始终以和结束。这就是 Promise.all 被设计的原因。当然有人会说我们可以使用process.on('unhandledRejection', err => {})and来抑制错误,process.on('rejectionHandled', err => {})但这不是一个好习惯。我在互联网上发现了许多示例,它们根本不考虑两个或多个独立异步并行任务的错误处理,或者以错误的方式考虑它——只是使用 try/catch 并希望它会捕获错误。几乎不可能找到好的做法。这就是我写这个答案的原因。
Summary
概括
Never use multiple await for two or more independent async parallel tasks because of you will not be able to handle errors seriously. Always use Promise.all() for this use case.Async/await is not replacement for Promises. It is just pretty way how to use promises... async code is written in sync styleand we can avoid multiple thenin promises.
切勿对两个或多个独立的异步并行任务使用多个等待,因为您将无法认真处理错误。对于此用例,始终使用 Promise.all()。Async/await 不能替代 Promise。这只是如何使用 Promise 的一种很好的方式……异步代码是以同步风格编写的,我们可以避免then在 Promise 中使用多个。
Some people say that using Promise.all() we can not handle tasks errors separately but only error from first rejected promise (yes, some use cases may require separate handling e.g. for logging). It is not problem - see "Addition" heading below.
有人说使用 Promise.all() 我们不能单独处理任务错误,而只能处理来自第一个被拒绝的承诺的错误(是的,某些用例可能需要单独处理,例如日志记录)。这不是问题 - 请参阅下面的“添加”标题。
Examples
例子
Consider this async task...
考虑这个异步任务......
const task = function(taskNum, seconds, negativeScenario) {
return new Promise((resolve, reject) => {
setTimeout(_ => {
if (negativeScenario)
reject(new Error('Task ' + taskNum + ' failed!'));
else
resolve('Task ' + taskNum + ' succeed!');
}, seconds * 1000)
});
};
When you run tasks in positive scenario there is no difference between Promise.all and multiple await. Both examples end with Task 1 succeed! Task 2 succeed!after 5 seconds.
当您在积极的场景中运行任务时,Promise.all 和多个等待之间没有区别。两个示例都Task 1 succeed! Task 2 succeed!在 5 秒后结束。
// Promise.all alternative
const run = async function() {
// tasks run immediate in parallel and wait for both results
let [r1, r2] = await Promise.all([
task(1, 5, false),
task(2, 5, false)
]);
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: Task 1 succeed! Task 2 succeed!
// multiple await alternative
const run = async function() {
// tasks run immediate in parallel
let t1 = task(1, 5, false);
let t2 = task(2, 5, false);
// wait for both results
let r1 = await t1;
let r2 = await t2;
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: Task 1 succeed! Task 2 succeed!
When first task takes 10 seconds in positive scenario and seconds task takes 5 seconds in negative scenario there are differences in errors issued.
当第一个任务在正面场景中需要 10 秒而在负面场景中第二个任务需要 5 秒时,发出的错误会有所不同。
// Promise.all alternative
const run = async function() {
let [r1, r2] = await Promise.all([
task(1, 10, false),
task(2, 5, true)
]);
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// multiple await alternative
const run = async function() {
let t1 = task(1, 10, false);
let t2 = task(2, 5, true);
let r1 = await t1;
let r2 = await t2;
console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
// at 10th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
We should already notice here that we are doing something wrong when using multiple await in parallel. Of course to avoid errors we should handle it! Let's try...
我们应该已经注意到,在并行使用多个 await 时我们做错了。当然,为了避免错误,我们应该处理它!咱们试试吧...
// Promise.all alternative
const run = async function() {
let [r1, r2] = await Promise.all([
task(1, 10, false),
task(2, 5, true)
]);
console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: Caught error Error: Task 2 failed!
As you can see to successfully handle error we need to add just one catch to runfunction and code with catch logic is in callback (async style). We do not need handle errors inside runfunction because async function it does automatically - promise rejection of taskfunction causes rejection of runfunction. To avoid callback we can use sync style(async/await + try/catch) try { await run(); } catch(err) { }but in this example it is not possible because we can not use awaitin main thread - it can be used only in async function (it is logical because nobody wants to block main thread). To test if handling works in sync stylewe can call runfunction from another async function or use IIFE (Immediately Invoked Function Expression): (async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();.
如您所见,要成功处理错误,我们只需要向run函数添加一个 catch,并且带有 catch 逻辑的代码在回调中(异步样式)。我们不需要处理run函数内部的错误,因为异步函数会自动执行 - 承诺拒绝task函数会导致拒绝run函数。为了避免回调,我们可以使用同步样式(async/await + try/catch),try { await run(); } catch(err) { }但在这个例子中这是不可能的,因为我们不能await在主线程中使用- 它只能在异步函数中使用(这是合乎逻辑的,因为没有人想要阻塞主线程)。要测试处理是否以同步方式工作,我们可以调用run从另一异步功能或用途IIFE(立即调用函数表达式)函数:(async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();。
This is only one correct way how to run two or more async parallel tasks and handle errors. You should avoid examples below.
这只是如何运行两个或多个异步并行任务和处理错误的一种正确方法。你应该避免下面的例子。
// multiple await alternative
const run = async function() {
let t1 = task(1, 10, false);
let t2 = task(2, 5, true);
let r1 = await t1;
let r2 = await t2;
console.log(r1 + ' ' + r2);
};
We can try to handle code above several ways...
我们可以尝试通过几种方式来处理上面的代码...
try { run(); } catch(err) { console.log('Caught error', err); };
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled
... nothing got caught because it handles sync code but runis async
...没有被捕获,因为它处理同步代码但是run是异步的
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: Caught error Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
... Wtf? We see firstly that error for task 2 was not handled and later that was caught. Misleading and still full of errors in console. Unusable this way.
…… 靠?我们首先看到任务 2 的错误没有被处理,后来被捕获。控制台中仍然充满误导性和错误。不能用这种方式。
(async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: Caught error Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
... the same as above. User @Qwerty in his deleted answer asked about this strange behavior that seems to be catched but there are also unhandled errors. We catch error because run() is rejected on line with await keyword and can be catched using try/catch when calling run(). We also get unhandled error because we are calling async task function synchronously (without await keyword) and this task runs outside run() function and also fails outside. It is similar when we are not able to handle error by try/catch when calling some sync function which part of code runs in setTimeout... function test() { setTimeout(function() { console.log(causesError); }, 0); }; try { test(); } catch(e) { /* this will never catch error */ }.
……和上面一样。用户@Qwerty 在他删除的答案中询问了这种奇怪的行为,该行为似乎已被捕获,但也存在未处理的错误。我们捕获错误是因为 run() 被 await 关键字拒绝,并且可以在调用 run() 时使用 try/catch 捕获。我们也会得到未处理的错误,因为我们正在同步调用异步任务函数(没有 await 关键字)并且该任务在 run() 函数之外运行并且在外部也失败。当我们在调用某些同步函数时无法通过 try/catch 处理错误时,该函数的一部分在 setTimeout... 中运行时,情况类似function test() { setTimeout(function() { console.log(causesError); }, 0); }; try { test(); } catch(e) { /* this will never catch error */ }。
const run = async function() {
try {
let t1 = task(1, 10, false);
let t2 = task(2, 5, true);
let r1 = await t1;
let r2 = await t2;
}
catch (err) {
return new Error(err);
}
console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
... "only" two errors (3rd one is missing) but nothing caught.
...“只有”两个错误(第三个缺失)但没有发现任何错误。
Addition (handle task errors separately and also first-fail error)
添加(单独处理任务错误和首次失败错误)
const run = async function() {
let [r1, r2] = await Promise.all([
task(1, 10, true).catch(err => { console.log('Task 1 failed!'); throw err; }),
task(2, 5, true).catch(err => { console.log('Task 2 failed!'); throw err; })
]);
console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Run failed (does not matter which task)!'); });
// at 5th sec: Task 2 failed!
// at 5th sec: Run failed (does not matter which task)!
// at 10th sec: Task 1 failed!
... note that in this example I used negativeScenario=true for both tasks for better demonstration what happens (throw erris used to fire final error)
...请注意,在这个例子中,我对两个任务都使用了 negativeScenario=true 以更好地演示会发生什么(throw err用于触发最终错误)
回答by zpr
You can check for yourself.
你可以自己查一下。
In this fiddle, I ran a test to demonstrate the blocking nature of await, as opposed to Promise.allwhich will start all of the promises and while one is waiting it will go on with the others.
在这个小提琴中,我运行了一个测试来演示 的阻塞性质await,而不是Promise.all它会启动所有的承诺,而当一个人在等待时,它会继续其他人。
回答by GavinBelson
Generally, using Promise.all()runs requests "async" in parallel. Using awaitcan run in parallel OR be "sync" blocking.
通常,使用Promise.all()并行运行请求“异步”。使用await可以并行运行或“同步”阻塞。
test1and test2functions below show how awaitcan run async or sync.
下面的test1和test2函数显示了如何await运行异步或同步。
test3shows Promise.all()
test3显示Promise.all()
jsfiddle with timed results- open browser console to see test results
带有定时结果的 jsfiddle- 打开浏览器控制台查看测试结果
Syncbehavior. Does NOT run in parallel, takes ~1800ms:
同步行为。不并行运行,需要约1800 毫秒:
const test1 = async () => {
const delay1 = await Promise.delay(600); //runs 1st
const delay2 = await Promise.delay(600); //waits 600 for delay1 to run
const delay3 = await Promise.delay(600); //waits 600 more for delay2 to run
};
Asyncbehavior. Runs in paralel, takes ~600ms:
异步行为。并行运行,大约需要600 毫秒:
const test2 = async () => {
const delay1 = Promise.delay(600);
const delay2 = Promise.delay(600);
const delay3 = Promise.delay(600);
const data1 = await delay1;
const data2 = await delay2;
const data3 = await delay3; //runs all delays simultaneously
}
Asyncbehavior. Runs in parallel, takes ~600ms:
异步行为。并行运行,大约需要600 毫秒:
const test3 = async () => {
await Promise.all([
Promise.delay(600),
Promise.delay(600),
Promise.delay(600)]); //runs all delays simultaneously
};
TLDR; If you are using Promise.all()it will also "fast-fail" - stop running at the time of the firstfailure of any of the included functions.
TLDR;如果您正在使用Promise.all()它,它也会“快速失败” - 在任何包含的功能第一次失败时停止运行。
回答by Waleed Naveed
In case of await Promise.all([task1(), task2()]);"task1()" and "task2()" will run parallel and will wait until both promises are completed (either resolved or rejected). Whereas in case of
如果是await Promise.all([task1(), task2()]); “task1()”和“task2()”将并行运行并等待两个promise完成(解决或拒绝)。而在情况下
const result1 = await t1;
const result2 = await t2;
t2 will only run after t1 has finished execution (has been resolved or rejected). Both t1 and t2 will not run parallel.
t2 只会在 t1 完成执行(已解决或拒绝)后运行。t1 和 t2 不会并行运行。

