Hid*_*den 127 javascript async-await
之间有什么区别:
const [result1, result2] = await Promise.all([task1(), task2()]);
Run Code Online (Sandbox Code Playgroud)
和
const t1 = task1();
const t2 = task2();
const result1 = await t1;
const result2 = await t2;
Run Code Online (Sandbox Code Playgroud)
和
const [t1, t2] = [task1(), task2()];
const [result1, result2] = [await t1, await t2];
Run Code Online (Sandbox Code Playgroud)
zzz*_*Bov 155
出于本答案的目的,我将使用一些示例方法:
await
是一个函数,它占用整数毫秒并返回一个在很多毫秒后解析的promise.Promise.all
是一个采用整数毫秒的函数,并返回一个在很多毫秒后拒绝的promise.呼叫res(ms)
启动计时器.rej(ms)
在所有延迟完成后,使用等待一些延迟将解决,但请记住它们同时执行:
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
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
这意味着res
将在3秒后使用来自内部承诺的数据解析.
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
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
如果您Promise.all
改为使用,则必须等待每个承诺按顺序解决,这可能效率不高:
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
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
mik*_*kep 43
我赞同@ zzzzBov的答案,但Promise.all的"快速失败"优势不仅仅是一个区别.评论中的一些用户询问为什么在负面情况下(当某些任务失败时)只使用Promise.all时更快.我问为什么不呢?如果我有两个独立的异步并行任务,第一个在很长一段时间内解决,但第二个在很短的时间内被拒绝,为什么让用户等待错误信息"很长时间"而不是"非常短的时间"?在现实生活中,我们必须考虑消极情景.但是好的 - 在第一个区别中,您可以决定使用Promise.all与多个等待的替代方案.
但在考虑错误处理时,你必须使用Promise.all.无法正确处理多个await触发的异步并行任务的错误.在否定情况下,你将永远与结束UnhandledPromiseRejectionWarning
和PromiseRejectionHandledWarning
虽然你使用try/catch语句的任何地方.这就是Promise.all的设计原因.当然有人可以说我们可以使用process.on('unhandledRejection', err => {})
和抑制错误,process.on('rejectionHandled', err => {})
但这不是好的做法.我在互联网上发现了许多例子,它们根本没有考虑对两个或多个独立的异步并行任务进行错误处理,或者以错误的方式考虑它 - 只是使用try/catch并希望它会捕获错误.找到好的做法几乎是不可能的.这就是我写这个答案的原因.
永远不要使用多个await来执行两个或更多个独立的异步并行任务,因为您将无法认真处理错误.始终对此用例使用Promise.all().
Async/await不是Promises的替代品.这是如何使用promises的漂亮方式...异步代码是以同步方式编写的,我们可以避免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)
});
};
Run Code Online (Sandbox Code Playgroud)
在正面场景中运行任务时,Promise.all和多个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!
Run Code Online (Sandbox Code Playgroud)
// 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!
Run Code Online (Sandbox Code Playgroud)
当第一个任务在正面情况下需要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!
Run Code Online (Sandbox Code Playgroud)
// 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!
Run Code Online (Sandbox Code Playgroud)
我们应该已经注意到,当并行使用多个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!
Run Code Online (Sandbox Code Playgroud)
正如您所看到的那样,为了成功处理错误,我们只需要为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); }; })();
.
这只是一种正确的方法,可以运行两个或多个异步并行任务并处理错误.你应该避免下面的例子.
// 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 Code Online (Sandbox Code Playgroud)
我们可以尝试以上几种方式处理代码......
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 Code Online (Sandbox Code Playgroud)
...没有被捕获,因为它处理同步代码但是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)
Run Code Online (Sandbox Code Playgroud)
...... Wtf?我们首先看到任务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)
Run Code Online (Sandbox Code Playgroud)
......和上面一样.
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)
Run Code Online (Sandbox Code Playgroud)
......"只有"两个错误(第三个错过)但没有抓到.
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!
Run Code Online (Sandbox Code Playgroud)
...请注意,在此示例中,我对两个任务使用了negativeScenario = true,以便更好地演示发生的情况(function test() { setTimeout(function() { console.log(causesError); }, 0); }; try { test(); } catch(e) { /* this will never catch error */ }
用于触发最终错误)
Gav*_*son 29
通常,使用Promise.all()
并行运行请求“异步”。使用await
可以并行运行或“同步”阻塞。
下面的test1和test2函数显示了如何await
运行异步或同步。
test3显示Promise.all()
这是异步的。
带有定时结果的 jsfiddle - 打开浏览器控制台查看测试结果
同步行为。不并行运行,需要约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
};
Run Code Online (Sandbox Code Playgroud)
异步行为。并行运行,大约需要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
}
Run Code Online (Sandbox Code Playgroud)
异步行为。并行运行,大约需要600 毫秒:
const test3 = async () => {
await Promise.all([
Promise.delay(600),
Promise.delay(600),
Promise.delay(600)]); //runs all delays simultaneously
};
Run Code Online (Sandbox Code Playgroud)
TLDR;如果您正在使用Promise.all()
它,它也会“快速失败”——在任何包含的功能第一次失败时停止运行。
您可以自己检查。
在这个小提琴中,我进行了一个测试,以证明的阻止性质,与之await
相反Promise.all
,它将开始所有的承诺,而在一个等待时它将与其他承诺继续进行。
归档时间: |
|
查看次数: |
68538 次 |
最近记录: |