javascript async await_如何简化异步代码

async/await 能替代 Promise.then 且更直观,本质是 Promise 的语法包装;await 只能在 async 函数中使用,顶层 await 需 ES 模块支持;需用 try/catch 正确捕获错误,注意并发控制与错误隔离。

async/await 是什么,它真能替代 Promise.then?

能,而且更直观。async/await 不是新语法糖,而是 Promise 的语法包装,底层完全基于 Promise。只要函数用 async 声明,它就自动返回一个 Promise;在函数体内用 await 等待的值,无论原本是不是 Promise,都会被自动 Promise.resolve() 包裹。

await 只能在 async 函数里用,但顶层 await 有例外

直接在模块顶层写 await 会报错 SyntaxError: await is only valid in async function,除非你:

  • 在 ES 模块(.mjs 文件或 type="module" 的 script 标签中)启用顶层 await —— 这是合法的,且浏览器和 Node.js 14.8+ 都支持
  • 用 IIFE 包一层:
    ;(async () => {
      const data = await fetch('/api/user').then(r => r.json())
      console.log(data)
    })()
  • 不滥用:多数业务逻辑仍应封装进 async 函数,而非依赖顶层 await

try/catch 比 .catch() 更自然,但别漏掉错误边界

await 后抛出的错误不会冒泡到外层同步代码,必须用 try/catch 捕获。这比链式 .catch() 更贴近同步思维,但也容易误以为“没写 catch 就不会出错”。

常见疏忽:

  • 忘记包裹整个 await 序列,比如只包了第一个 await fetch(),但后续 res.json() 可能失败,也要在同一个 try 块里
  • 在循环中 await 多个请求时,没做错误隔离,一个失败导致整个流程中断 —— 此时应配合 Promise.allSettled() 或单独 try/catch 每次调用
  • await 后接的是可能为 undefinednull 的值时,直接链式调用会报 Cannot read property 'xxx' of undefined,这不是 Promise rejection,catch 捕不到

并发请求别盲目 await,注意串行 vs 并行

连续写多个 await 是串行执行,耗时累加;想并行,得先发起所有 Promise,再统一 await:

const [user, posts, comments] = await Promise.all([
  fetch('/api/user').then(r => r.json()),
  fetch('/api/posts').then(r => r.json()),
  fetch('/api/comments').then(r => r.json())
])

关键点:

  • Promise.all() 任意一个 reject 整体就失败;需要全部结果(含失败)请改用 Promise.allSettled()
  • Node.js 中 await 后跟普通函数调用(如 await fs.readFile())仍是异步,但若该函数内部没真正异步(比如只是同步计算),await 就失去意义,还可能误导可读性
  • V8 引擎对 async 函数有额外开销,高频小函数(如每帧调用的渲染逻辑)慎用,优先考虑纯同步或 Promise 缓存
实际写法中,最常被忽略的是错误传播路径和并发控制粒度——不是所有 await 都该平铺直写,也不是每个异步操作都值得单独拎成 async 函数。