为何第二个JS异步函数调用需等待第一个执行完成?
为什么第二个async函数调用要等第一个完成,第三个却不用?
嘿,这个问题其实戳中了async/await里很多人都会踩的坑,咱先把核心逻辑拆解清楚,你就能瞬间明白啦~
首先得明确两个关键事实:
- 调用async函数本身是同步行为:不管你加不加
await,只要直接调用async函数,它内部的同步代码会立刻在主线程执行,这部分是会阻塞后续同步任务的,只有碰到await(本质是等待Promise)时,才会让出主线程,把后续逻辑放到微任务队列。 - JavaScript是单线程的:所有同步任务都在主线程的执行栈里按顺序排队,异步任务(比如
setTimeout这种宏任务、Promise回调这种微任务)得等执行栈空了之后才会被处理。
结合你的场景(基于常见的示例逻辑)
假设你的代码大概是这个样子:
async function longRunningAsync() { console.log('第一个函数启动'); // 模拟同步耗时操作(比如大循环、复杂计算) for (let i = 0; i < 100000000; i++) {} console.log('第一个函数结束'); // 就算有await,也是同步代码跑完才触发异步逻辑 await Promise.resolve(); } async function quickAsync() { console.log('第二个函数启动'); console.log('第二个函数结束'); } async function delayedAsync() { console.log('第三个函数启动'); console.log('第三个函数结束'); } // 调用顺序 longRunningAsync(); quickAsync(); setTimeout(() => delayedAsync(), 0);
为啥第二个调用要等第一个?
当你执行longRunningAsync()时,函数会立刻跑内部的同步代码:先打印“第一个函数启动”,然后开始那个超大循环——这部分是实打实的同步阻塞,主线程被这个循环占得死死的,根本没机会去执行下一个同步任务quickAsync()的调用。只有等longRunningAsync里的所有同步代码都跑完(打印“第一个函数结束”),主线程的执行栈空出位置,才会轮到quickAsync()执行它的同步代码。
这里的核心是:直接调用async函数属于同步任务队列里的操作,会按调用顺序依次执行,前面的同步代码没跑完,后面的就只能等着。
为啥第三个调用不用等?
第三个函数是通过setTimeout来调用的,setTimeout属于宏任务。宏任务会被放到专门的宏任务队列里,只有当主线程的执行栈完全清空,并且所有微任务都执行完毕之后,才会去处理宏任务队列里的任务。
所以当longRunningAsync和quickAsync的同步代码全部跑完,主线程彻底空闲了,才会去执行setTimeout的回调,调用delayedAsync()——这时候它的执行自然不会被前面的函数阻塞了。
最后再划个重点
- 直接调用async函数,它的同步代码会立刻阻塞主线程,后续的同步任务(比如第二个直接调用的async函数)必须等它的同步代码跑完才能执行。
- 如果把async函数的调用放到异步任务(比如
setTimeout、Promise回调)里,它会等到主线程空了之后才执行,不会被前面的同步任务阻塞。 await的作用是让当前函数的后续代码等待Promise完成,它只会让出当前函数的执行权,不会阻塞整个主线程——这和你看到的“第二个调用等待第一个”是完全不同的场景哦。
内容的提问来源于stack exchange,提问作者Oscar Rhodes
相关产品推荐
相关产品推荐

