在一个 async 函数中,同时调用两个 await,经常是一个写在另外一个后面,这种方式是不对的。
Talk is cheap, show me the code.
function getResult() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('result')
}, 1000);
});
}
async function test() {
console.time();
// 1. Using Promise.all
Promise.all([getResult(), getResult()])
.then(([result1, result2]) => {
console.log(result1, result2);
console.timeEnd(); // default: 1017.324ms
})
// 还有另外一种写法是
// const [result1, result2] = await Promise.all([getResult(), getResult()]);
// console.log(result1, result2);
// console.timeEnd(); // 1000.00 ms
// 2. Using await
// const result1 = await getResult();
// const result2 = await getResult();
// console.log(result1, result2);
// console.timeEnd(); // default: 2014.548ms
}
test();
应该用第一种方式,第二种方式表示返回 result1 之后,再执行 getResult 来获得 result2,就是串行了。但实际上大部分的情况都应该是并行的。
generator
这里在简单说下 generator 的内容。 generator 实际上是创建协程的过程,而 async/await 实际上是对 generator 和 promise 的封装(语法糖)。 那么 async 函数执行的时候实际上是创建 协程,而 await 的时候实际上是讲子协程返回给父协程的过程,这也就能解释为什么上面的那种 await 分开两行写会导致串行的原因了。
async function foo() { // 3. 创建 子协程 foo,并继续执行 foo 协程中的内容
console.log(1); // 4. 在执行 foo 协程的过程中,没有遇到 await 就正常按顺序执行该协程的内容
const a = await 100; // 5. 遇到 await 则挂起当前 foo 协程,在微任务队列中添加任务,返回主协程,暂停后面的执行
console.log(a); // 7. 执行已经创建的微任务,继续执行 foo 协程中的内容,没有遇到 await 之前都在此协程中正常按顺序执行
const b = await 200; // 8. 创建微任务,push 进队列中,返回主协程,挂起当前子协程,也就是暂停后面的执行
console.log(b); // 10. 执行微任务,继续执行 foo 协程中的内容,没有遇到 await 之前都在此协程中正常按顺序执行
console.log(2); // 11. foo 协程执行结束,返回主协程
}
console.log(0); // 1. 执行主线程内容
foo(); // 2. 执行 foo 函数
console.log(3); // 6. 执行主协程的内容
// 9. 主协程没有任务内容,返回子协程
// 12. 主协程没有任务内容,执行完毕
实际上 mdn 对 generator 做了很详细的说明
Calling a generator function does not execute its body immediately; an iterator object for the function is returned instead. When the iterator's next() method is called, the generator function's body is executed until the first yield expression, which specifies the value to be returned from the iterator or, with yield*, delegates to another generator function. The next() method returns an object with a value property containing the yielded value and a done property which indicates whether the generator has yielded its last value, as a boolean. Calling the next() method with an argument will resume the generator function execution, replacing the yield expression where an execution was paused with the argument from next().