JavaScript中await的作用、执行时机及与微任务队列的关联
理解JavaScript中await的作用与微任务队列的关系
我是JavaScript新手,想要理解await的具体作用及其与微任务队列的关系。以下为用于说明问题的代码示例:
async function afunc() { console.log("a1"); const b = bfunc(); console.log("a2"); b.then((value) => { console.log(value); }); return "a value"; } async function bfunc() { console.log("b1"); await console.log("waiting"); console.log("b2"); return "b value"; } const a = afunc(); a.then((value) => { console.log(value); }); console.log("script end");
执行结果为:
a1 b1 waiting a2 script end b2 a value b value
MDN中提到:
await表达式会暂停async函数的执行,直到Promise状态敲定(已兑现或已拒绝),然后在兑现后恢复函数执行。
我有以下疑问:
- 这个“恢复执行”具体发生在什么时候?
- 它与微任务队列的执行顺序有什么关联?
- 我原本认为b.then()会比a.then()更早进入微任务队列,所以b value应该出现在a value之前,但实际输出与预期不符,对此感到困惑。
补充说明:如果移除bfunc中的await,执行结果变为:
a1 b1 waiting b2 a2 script end b value a value
解答
1. “恢复执行”的时机
当await后跟一个非Promise值(比如这里的console.log("waiting"),它返回undefined),JS会把这个值包装成一个已兑现的Promise。await暂停当前async函数后,会把函数后续的代码(也就是console.log("b2"); return "b value";)打包成一个微任务,添加到微任务队列里。
这个微任务会在当前同步执行栈清空后,下一次宏任务执行前被取出执行——也就是等所有同步代码跑完,才会轮到这个微任务恢复bfunc的执行。
2. await与微任务队列的关联
await本质就是在操控微任务队列:
- 遇到
await时,当前async函数暂停,后续代码被封装成微任务入队。 - 微任务队列遵循先进先出规则,但必须等当前同步栈的所有代码执行完毕后,才会一次性清空所有已存在的微任务(如果微任务执行中又产生新的微任务,也会被加入队列,继续执行直到队列空)。
3. 为什么a value在b value前面?
我们一步步拆解原代码的执行流程:
- 同步执行
const a = afunc();,进入afunc:- 打印
a1,然后调用bfunc()。
- 打印
- 进入bfunc:
- 打印
b1,执行await console.log("waiting")——打印waiting,然后把bfunc后续的console.log("b2"); return "b value";包装成微任务A,加入微任务队列。此时bfunc暂停,返回一个pending状态的Promise给变量b。
- 打印
- 回到afunc:
- 打印
a2,然后给b这个pending的Promise绑定then回调(这个回调要等b的Promise兑现后才会入队),接着afunc返回"a value",此时afunc的Promise状态变为已兑现,它的then回调(打印a value)被包装成微任务B,加入微任务队列。
- 打印
- 同步执行
console.log("script end");,此时同步栈清空。 - 开始处理微任务队列:
- 第一个微任务是A:执行bfunc的剩余代码,打印
b2,然后把b的Promise状态改为已兑现。这时候,之前绑定的b.then()回调(打印b value)被包装成微任务C,加入队列。 - 接着执行微任务B:打印
a value。 - 最后执行微任务C:打印
b value。
- 第一个微任务是A:执行bfunc的剩余代码,打印
所以最终顺序是b2→a value→b value,和你看到的结果一致。
而当你移除bfunc中的await时,bfunc变成了没有暂停的async函数:
- 调用bfunc时会同步执行完所有代码:打印
b1→waiting→b2,直接返回已兑现的Promise"b value"。 - 回到afunc,
b.then()的回调会立即被加入微任务队列(因为b的Promise已经兑现),而afunc的then回调是在afunc返回后才入队。所以微任务队列里顺序是b.then()回调在前,a.then()在后,执行时就先打印b value再打印a value。
内容的提问来源于stack exchange,提问作者wang9hu
相关产品推荐
相关产品推荐

