You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何第二个JS异步函数调用需等待第一个执行完成?

为什么第二个async函数调用要等第一个完成,第三个却不用?

嘿,这个问题其实戳中了async/await里很多人都会踩的坑,咱先把核心逻辑拆解清楚,你就能瞬间明白啦~

首先得明确两个关键事实:

  1. 调用async函数本身是同步行为:不管你加不加await,只要直接调用async函数,它内部的同步代码会立刻在主线程执行,这部分是会阻塞后续同步任务的,只有碰到await(本质是等待Promise)时,才会让出主线程,把后续逻辑放到微任务队列。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:05:44