异步函数中Promise的.then()是否优先于后续同步代码执行?
结论先行:这个结果绝对不是偶然,
.then()分支一定会优先于console.log(x)执行 你完全可以放心——你的代码逻辑是符合JavaScript异步机制的,console.log(x)永远会在.then()里的x=100之后执行,不会出现输出0的情况。下面我拆解一下背后的执行逻辑,帮你彻底搞清楚:
核心原因:await等待的是整个Promise链的完成
你代码里的await并不是只等待最初那个由setTimeout创建的Promise,而是等待**new Promise(...).then(...)这条链式调用返回的新Promise**。
Promise的.then()方法有个关键特性:它会返回一个全新的Promise,这个新Promise的状态只有在.then()的回调函数执行完毕后,才会变为resolved。而await的作用就是暂停当前async函数的执行,直到它等待的Promise状态变为resolved。
换句话说,只有当.then()里的x=100执行完成,整个链式Promise才算走完,async函数才会恢复执行,继续运行后面的console.log(x)。
详细执行步骤拆解
我们一步步走一遍代码的运行流程:
- 立即执行async函数,
x被初始化为0。 - 创建第一个Promise,里面的
setTimeout被加入宏任务队列,等待2秒后触发。 await暂停当前async函数,把执行权交还给主线程。- 2秒后,
setTimeout的回调触发,调用resolve(),第一个Promise变为resolved状态。 - 此时,
.then()的回调(x=100)被加入微任务队列,等待当前宏任务执行完毕后立即执行。 .then()回调执行,x被赋值为100,同时链式Promise的状态变为resolved。- async函数的暂停被解除,继续执行
await后面的console.log(x),此时x已经是100了。
关于你提到的几个现象的解释
- 添加大量同步代码不影响结果:同步代码会在当前宏任务中执行,而
.then()属于微任务,会在当前宏任务结束后、下一个宏任务之前执行。但因为await等待的是整个链式Promise,所以不管同步代码有多少,只要链式Promise没走完,async函数就不会继续,console.log(x)永远排在.then()之后。 - 异步代码(如另一个
setTimeout)可能影响结果:只有当你把赋值逻辑放到另一个宏任务(比如额外的setTimeout)里,而不是.then()的微任务中时,才会出现console.log(x)先执行的情况——因为新的宏任务会排在下一轮事件循环,而await不会等待这个额外的宏任务。
更简洁的写法(供参考)
虽然你的代码是安全的,但确实可以去掉.then()让逻辑更直观:
(async function() { let x = 0; // 等待Promise完成后,直接在async函数里赋值 await new Promise((resolve, reject) => { setTimeout(() => resolve(), 2000); }); x = 100; console.log(x); })();
内容的提问来源于stack exchange,提问作者Tamás Polgár
相关产品推荐
相关产品推荐

