第一个区别 - 快速失败
我同意@zzzzBov的答案,但“快速失败”的优势并不是唯一的区别。评论中的一些用户问为什么使用是值得的,因为它只是在消极的情况下(当某些任务失败时)更快。我问,为什么不呢?如果我有两个独立的异步并行任务,第一个任务需要很长时间才能解决,但第二个任务在很短的时间内被拒绝,为什么让用户等待较长的调用完成以接收错误消息?在实际应用中,我们必须考虑负面情况。但是好的 - 在第一个差异中,您可以决定使用哪种替代方案:vs. 多个 。Promise.all
Promise.all
Promise.all
await
第二个区别 - 错误处理
但是在考虑错误处理时,必须使用 .无法正确处理由多个 触发的异步并行任务的错误。在消极的情况下,无论你在哪里使用 try/catch,你总是会以 和 结尾。这就是设计的原因。当然,有人会说我们可以抑制这些错误 使用,但这不是好的做法。我在互联网上发现许多例子,它们根本不考虑两个或多个独立的异步并行任务的错误处理,或者考虑它但以错误的方式 - 只是使用try/catch并希望它能捕获错误。几乎不可能在这方面找到好的做法。Promise.all
await
UnhandledPromiseRejectionWarning
PromiseRejectionHandledWarning
Promise.all
process.on('unhandledRejection', err => {})
process.on('rejectionHandled', err => {})
总结
TL;DR:切勿对两个或多个独立的异步并行任务使用多个 await
,因为您将无法正确处理错误。对于此用例,请始终使用 Promise.all()。
Async/不是 Promises 的替代品,它只是一种使用 Promise 的漂亮方式。异步代码是以“同步风格”编写的,我们可以避免在承诺中多次使用。await
then
有人说,在使用时,我们不能单独处理任务错误,并且我们只能处理第一个被拒绝的承诺中的错误(单独处理可能很有用,例如用于日志记录)。这不是问题 - 请参阅此答案底部的“添加”标题。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)
});
};
在正方案中运行任务时,和多个 s 之间没有区别。两个示例都以 5 秒后结束。Promise.all
await
Task 1 succeed! Task 2 succeed!
// 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!
我们应该已经注意到,在并行使用多个 s 时,我们做错了什么。让我们尝试处理错误: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!
如您所见,要成功处理错误,我们只需要向函数添加一个 catch,并将具有 catch 逻辑的代码添加到回调中。我们不需要处理函数内部的错误,因为异步函数会自动执行此操作 - 承诺拒绝函数会导致函数被拒绝。run
run
task
run
为了避免回调,我们可以使用“sync style”(async/ + try/ catch
),
但在此示例中这是不可能的,因为我们不能在主线程中使用 - 它只能在异步函数中使用(因为没有人想要阻塞主线程)。为了测试处理是否以“同步风格”工作,我们可以从另一个异步函数调用该函数或使用IIFE(立即调用的函数表达式:MDN):await
try { await run(); } catch(err) { }
await
run
(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在他删除的答案中询问了这种奇怪的行为,其中错误似乎被捕获,但也未处理。我们捕获错误,因为在带有关键字的行上被拒绝,并且可以在调用时使用try/ catch捕获。我们还会收到一个未处理的错误,因为我们同步调用异步任务函数(不带关键字),并且此任务在函数外部运行并失败。
这类似于当我们在调用一些调用 setTimeout 的 sync 函数时,无法通过 try/catch 来处理错误:run()
await
run()
await
run()
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