之间有什么区别:
const [result1, result2] = await Promise.all([task1(), task2()]);
和
const t1 = task1();
const t2 = task2();
const result1 = await t1;
const result2 = await t2;
和
const [t1, t2] = [task1(), task2()];
const [result1, result2] = [await t1, await t2];
注意事项:
这个答案仅涵盖了
await
系列和系列之间的时序差异Promise.all
。请务必阅读@mikep的综合答案,其中也涵盖了错误处理方面更重要的区别。
出于此答案的目的,我将使用一些示例方法:
res(ms)
是一个函数,它需要一个整数毫秒,并返回一个承诺,该承诺将在该毫秒后解析。rej(ms)
是一个函数,它需要一个整数毫秒,并返回一个承诺,该承诺将在该毫秒后被拒绝。
调用将res
启动计时器。Promise.all
在所有延迟完成后,可以使用等待一些延迟来解决,但请记住它们是同时执行的:
例子1
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
这意味着Promise.all
将在3秒后使用内部承诺中的数据进行解析。
范例#2
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-await
改为使用,则必须等待每个promise依次解决,这可能不那么有效:
例子#3
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
第一个区别-快速失败
我同意@zzzzBov的回答,但是Promise.all
唯一的区别不是“快速失败”优势。评论中的一些用户问Promise.all
,如果在否定情况下(某些任务失败时)使用速度更快,为什么值得这样做。我问,为什么不呢?如果我有两个独立的异步并行任务,而第一个要花费很长的时间才能解决,而第二个任务却要在很短的时间内被拒绝,为什么要让用户等待更长的调用才能收到错误消息呢?在实际应用中,我们必须考虑负面情况。但是可以-在这第一个区别中,您可以决定使用哪个替代方法:Promise.all
vs await
。
第二点不同-错误处理
但是,在考虑错误处理时,您必须使用Promise.all
。无法正确处理由多个await
s触发的异步并行任务的错误。在否定情况下,无论在何处使用try / catch,都将始终以UnhandledPromiseRejectionWarning
和结尾PromiseRejectionHandledWarning
。这就是为什么Promise.all
设计的原因。当然有人会说我们可以使用来抑制这些错误process.on('unhandledRejection', err => {})
,process.on('rejectionHandled', err => {})
但这不是一个好习惯。我在互联网上发现了许多示例,这些示例根本不考虑针对两个或多个独立异步并行任务的错误处理,或者以错误的方式进行考虑-仅使用try / catch并希望它会捕获错误。在这方面几乎找不到好的做法。
概要
TL; DR:await
对于两个或多个独立的异步并行任务,请勿使用多个,因为您将无法正确处理错误。始终使用Promise.all()
此用例。
Async /await
不是Promises的替代品,它只是使用Promise的一种不错的方法。异步代码以“同步样式”编写,我们可以避免then
在promise中使用多个。
有人说,使用时Promise.all()
我们不能单独处理任务错误,而只能处理第一个被拒绝的承诺中的错误(分开处理可能有用,例如用于日志记录)。这不是问题-请参阅此答案底部的“添加”标题。
例子
考虑这个异步任务...
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)
});
};
当您在积极的情况下运行任务时,sPromise.all
与multiple之间没有区别await
。两个示例均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!
但是,当第一个任务花费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!
我们已经在这里注意到,当await
并行使用多个s时,我们做错了。让我们尝试处理错误:
// 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!
如您所见,要成功处理错误,我们只需要向run
函数添加一个catch并将带有catch逻辑的代码添加到回调中。我们不需要处理run
函数内部的错误,因为异步函数会自动执行此操作-答应拒绝该task
函数会导致该函数被拒绝run
。
为了避免回调,我们可以使用“同步样式”(async / await
+ try / catch),
try { await run(); } catch(err) { }
但是在此示例中这是不可能的,因为我们不能await
在主线程中使用-它只能在异步函数中使用(因为没人想要)阻止主线程)。要测试处理是否以“同步样式”工作,我们可以run
从另一个异步函数调用该函数,或者使用IIFE(立即调用函数表达式:MDN):
(async function() {
try {
await run();
} catch(err) {
console.log('Caught error', err);
}
})();
这是运行两个或多个异步并行任务并处理错误的唯一正确方法。您应避免使用以下示例。
错误的例子
// 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);
};
我们可以尝试以几种方式处理上面代码中的错误...
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
...什么也没抓住,因为它处理同步代码但是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)
嗯?我们首先看到任务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)
...与上述相同。用户@Qwerty在其已删除的答案中询问了这种奇怪的行为,在该行为中似乎捕获了错误但也未解决。我们捕获错误,因为run()
在await
关键字行中拒绝了因为,并且可以在调用时使用try / catch捕获run()
。我们还会收到未处理的错误,因为我们正在同步调用异步任务函数(没有await
关键字),并且此任务在run()
函数外部运行并失败。
这类似于在调用一些调用setTimeout的同步函数时无法通过try / catch处理错误的情况:
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)
...“只有”两个错误(缺少第三个错误),但没有发现任何错误。
加法(处理单独的任务错误以及首次失败错误)
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!
...请注意,在此示例中,我拒绝了这两个任务,以更好地演示发生了什么(throw err
用于引发最终错误)。
通常,使用Promise.all()
运行会并行请求“异步”。使用await
可以并行运行OR是“同步”阻断。
下面的test1和test2函数显示了如何await
运行异步或同步。
test3显示Promise.all()
这是异步的。
具有定时结果的jsfiddle- 打开浏览器控制台以查看测试结果
同步行为。不并行运行,大约需要1800ms:
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
};
异步行为。在运行相同常,大约需要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
}
异步行为。并行运行,大约需要600毫秒:
const test3 = async () => {
await Promise.all([
Promise.delay(600),
Promise.delay(600),
Promise.delay(600)]); //runs all delays simultaneously
};
TLDR;如果您使用Promise.all()
它,它还将“快速失败”-在任何包含的功能首次失败时停止运行。
您可以自己检查。
在这个小提琴中,我进行了一个测试,以证明的阻止性质,与之await
相反Promise.all
,它将开始所有的承诺,而在一个等待时它将与其他承诺继续进行。
如果等待Promise.all([task1(),task2()]); “ task1()”和“ task2()”将并行运行,并等待两个诺言都完成(已解决或被拒绝)。鉴于
const result1 = await t1;
const result2 = await t2;
t2仅在t1完成执行(已解决或拒绝)后运行。t1和t2都不会并行运行。
文章标签:async-await , javascript
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!
评论已关闭!