为何外部触发多个调用时async/await无法保证执行顺序?
为什么直接调用async函数会出现交错执行的现象?
这个问题问到点子上了,核心要搞懂两个关键点:async函数的执行逻辑和JavaScript的事件循环机制,咱们掰开揉碎了说:
1. 不使用await直接调用async函数时发生了什么?
当你写下doWork(1); doWork(2); doWork(3);的时候,每一次调用doWork都会同步执行函数里的代码,直到遇到第一个await:
- 调用
doWork(1):先同步打印Start 1,然后遇到await new Promise(res => setTimeout(res, 1000))。这时候会创建一个Promise,并把setTimeout的回调放进宏任务队列,然后直接暂停当前doWork(1)的执行,返回一个pending状态的Promise,主线程立刻继续执行下一行代码。 - 紧接着调用
doWork(2):同样同步打印Start 2,遇到await后暂停执行,主线程继续走。 - 再调用
doWork(3):同步打印Start 3,遇到await后暂停执行。
所以这一步下来,主线程同步执行完所有函数的前半段,就出现了先打印Start 1 Start 2 Start 3的情况。
2. 事件循环如何处理后续的End输出?
JavaScript是单线程的,主线程会先把当前所有同步代码执行完,才会去处理任务队列里的任务:
- 1秒后,第一个
setTimeout的回调触发,会把对应的Promise标记为resolved,这时候之前暂停的doWork(1)会从await的位置恢复执行,打印End 1。 - 紧接着第二个
setTimeout的回调触发(三个定时器几乎是同一时间被放进队列的,间隔都是1秒),doWork(2)恢复执行,打印End 2。 - 第三个定时器回调触发,
doWork(3)恢复执行,打印End 3。
这就解释了为什么会出现Start 1 Start 2 Start 3 End 1 End 2 End 3的交错执行结果。
3. 为什么使用await顺序调用时是串行的?
当你写await doWork(1); await doWork(2);的时候:
- 第一个
await会让主线程等待doWork(1)返回的Promise变为resolved,也就是要等doWork(1)完整执行完(从Start 1到End 1),才会继续执行下一行代码。 - 只有
doWork(1)完全结束后,才会调用doWork(2),重复同样的流程,所以执行顺序是严格的Start 1 → End 1 → Start 2 → End 2,完全可预测。
简单总结一下:await是让当前代码块等待async函数完成,而直接调用async函数只会启动它的同步部分,剩下的异步逻辑会交给事件循环处理,不会阻塞主线程,所以多个async函数会“同时”启动,最终出现交错的执行结果。
内容的提问来源于stack exchange,提问作者user10430344
相关产品推荐
相关产品推荐

