async 函数内部串行 ,外部非阻塞

5 阅读2分钟

这是一个非常经典的问题,很多人对 async/await 的“异步”机制存在误解。我们分两部分来回答:

1. async 代码是异步执行的吗?

结论:是的,但它是通过“非阻塞”的方式执行的。

当你调用一个 async 函数时:

  • 函数体内部的执行顺序: 在你遇到第一个 await 之前,代码是同步执行的。
  • 遇到 await 之后:
    • await 后面的表达式会立即执行。
    • 一旦执行到 await,函数会被“挂起”(暂停)。
    • JS 引擎会跳出这个 async 函数,去执行该函数外部的代码(这就是异步的体现)。
    • 等到 await 后面的异步操作(Promise)完成,JS 引擎再回到这个 async 函数中,恢复执行后面的代码。

2. 后面的方法会受影响吗?

这取决于你指的“后面”是什么:

情况 A:async 函数体内部“后面”的代码

受影响。 它们必须等待 await 这一行执行完成并拿到结果后,才会继续执行。这就像是“人为地把异步代码拉直成同步顺序”。

async function test() {
  console.log("1");
  await new Promise(r => setTimeout(r, 1000)); // 暂停 1 秒
  console.log("2"); // 这一行会被阻塞,直到 1 秒后才会执行
}
console.log("3");
test();
console.log("4");

// 执行顺序:1 -> 3 -> 4 -> (等待1秒) -> 2

情况 B:async 函数调用后的外部代码

不受影响(主线程不会被阻塞)。 这是 async/await 最核心的优势。虽然 async 函数内部被“暂停”了,但它不会阻塞整个 JavaScript 的执行线程

在上面的例子中,当 test() 执行到 await 暂停时,主线程立刻跳出 test 函数,继续去执行后面的 console.log("4")


总结:关键点对比

为了让你彻底明白,请记住这两个对比:

  1. 对于 async 函数内部: await 像是一道“锁”,它强迫后面的代码必须等待前面的异步操作完成。这让复杂的异步逻辑(如:先查数据库,再查缓存,最后返回数据)写起来像写同步代码一样简单。

  2. 对于整个程序: async/await非阻塞的。即使你在函数里写了 await 等待 10 秒,页面也不会卡死,其他的逻辑(如点击事件、动画、其他网络请求)依然可以正常运行。

一句话总结: async/await 只是在当前函数内部让代码看起来像同步,但在宏观上,它依然是异步的,不会阻塞主线程,也不会影响外部代码的正常执行。