如何正确使用 await 关键字让当前异步函数的执行上下文线程平滑挂起

作者:袖梨 2026-06-20
await挂起异步函数执行上下文而非线程,仅限async函数内使用;等待Promise或thenable对象,后续代码作为微任务执行,不阻塞主线程。

await 不会挂起“线程”,它挂起的是当前 异步函数的执行上下文,让出控制权给事件循环,以便其他任务可以运行。JavaScript 是单线程的,没有真正的线程概念(主线程上无多线程抢占),所以“挂起线程”这种说法容易引发误解。

await 只能在 async 函数内部使用

这是语法硬性要求。直接在普通函数或顶层作用域写 await somePromise() 会报语法错误。

  • 正确写法:async function foo() { await fetch('/api'); }
  • 错误写法:function bar() { await fetch('/api'); }(SyntaxError)
  • 顶层 await 仅限模块作用域(ESM 模块中允许),但不等同于在普通脚本中随意使用

await 等待的是“thenable”对象,本质是 Promise 状态流转

await 后面表达式会被自动 Promise.resolve() 包装。它真正等待的是该 Promise 进入 fulfilledrejected 状态。

  • await 123 → 等价于 await Promise.resolve(123),立刻继续执行
  • await fetch(url) → 等待网络响应完成并返回 Response 对象
  • await new Promise(r => setTimeout(r, 1000)) → 等待 1 秒后 resolve

await 之后的代码会被放入微任务队列,而非立即执行

当 await 后的 Promise 完成时,后续语句不会立刻同步运行,而是作为微任务(microtask)被调度,在当前同步代码结束后、下一次宏任务(如 setTimeout)之前执行。

  • 这意味着:await 不阻塞浏览器渲染或事件响应,UI 仍可交互
  • 多个 await 串行执行时,每个 await 都会“暂停”函数体,但不会冻结整个 JS 引擎
  • 例如:await a(); console.log('1'); await b(); console.log('2'); 中,'1' 一定在 a() 的 Promise fulfilled 后打印,'2' 同理

避免常见误用:不要用 await 包裹非 Promise 值做“伪等待”

比如 await setTimeout(...) 是无效的——setTimeout 返回的是定时器 ID(数字),不是 Promise,await 会立刻解包并继续,达不到延时效果。

  • 正确延时写法:await new Promise(r => setTimeout(r, 1000));
  • 错误写法:await setTimeout(() => {}, 1000);(毫无等待作用)
  • 对已 resolve 的 Promise 使用 await 无性能损失,但对纯同步值(如字符串、对象)使用 await 无实际意义

理解 await 的本质是“暂停函数执行、交还控制权、等待 Promise settled 后恢复”,比记住“挂起线程”更准确也更实用。

相关文章

精彩推荐