随着 JavaScript 异步编程成为现代前端与后端开发的核心能力,async/await 语法以其简洁、直观的特性,迅速取代了传统的回调地狱和冗长的 Promise 链式调用,成为开发者手中的利器。然而,利器虽好,若使用不当,反而会埋下隐蔽的性能陷阱或逻辑错误。近日,多位资深开发者分享了他们在 Code Review 中反复遇到的 8 个最常见的 async/await 错误,引发了行业内对编码规范的深度反思。

错误一:忘记添加 await,导致函数返回 Promise 而非结果

这是最基础却也最为普遍的错误。许多开发者在一个 async 函数内部调用另一个返回 Promise 的函数时,习惯性地省略 await,导致程序继续执行后续代码,而异步操作尚未完成。例如:

async function fetchData() {
  const data = getData(); // 缺失 await
  console.log(data); // 输出 Promise 对象,而非期望的数据
}

正确写法应为 const data = await getData();。这种疏忽在逻辑复杂、嵌套层级深时极易发生,并可能引发不可预期的竞态条件。

错误二:在 forEach 中使用 async/await,期望顺序执行

forEach 并不会等待 async 回调函数执行完毕,它会同时启动所有异步任务。若开发者希望逐个处理异步操作(如依次调用 API),使用 for...of 循环配合 await 才是正确选择。有开发者分享了一次惨痛教训:在批量写入数据库时使用 forEach + async/await,结果导致数据写入顺序错乱,部分记录因依赖关系出错。

错误三:忽略错误处理,try...catch 使用不当

部分开发者认为加了 async 的函数会自动捕获异常,实则不然。若未在顶层调用处包裹 try...catch,未被捕获的异常将导致进程崩溃(Node.js 环境)或 Promise 进入 rejected 状态未处理。更危险的是,有些开发者只在函数内部捕获错误,却忘记在外层调用处处理,使得错误信息被静默吞没。

错误四:在顶层代码顺序执行不必要的异步操作

在不依赖先后关系的场景下,使用 await 挨个执行异步任务会显著降低性能。例如同时请求多个无关联的 API 接口:

const user = await getUser();
const posts = await getPosts(); // 这两个操作本可以并行

正确做法是使用 Promise.all() 同时发起请求,再通过 await 等待所有结果返回。这一错误在代码审查中出现的频率极高,尤其是在初学 async/await 的开发者手中。

错误五:在非 async 函数中使用 await

await 只能在 async 函数内部使用。若在普通函数或全局作用域中使用 await,会抛出语法错误。Node.js 14.8+ 虽然支持顶层 await,但在浏览器或旧版本环境中依然受限。许多开发者在迁移旧代码时忘记将外层函数标识为 async,导致代码运行失败。

错误六:混用 async/await 与 .then() 链造成混淆

尽管语法上不会报错,但在一个函数内部同时使用 await.then().catch() 会使代码可读性急剧下降。团队应当统一编码规范:在 async 函数内部全程使用 awaittry...catch,而非混用两种风格。这种不统一的写法往往是在后期维护过程中,不同开发者反复修改留下的痕迹。

错误七:在构造函数或 getter 中使用 async

类构造函数不能是 async 函数,因为构造函数必须返回实例对象,而非 Promise。正确的做法是在实例化后手动调用一个 async 初始化方法,例如 new MyClass().init()。同样,getter 也不应被标记为 async,因为访问器属性期望返回同步值。若确实需要异步获取属性值,建议使用普通方法或 getAsyncData() 的形式。

错误八:过度使用 async,为不需要异步的函数添加 async 标记

当函数内部不包含任何 await 表达式时,标注 async 不仅多余,还会导致该函数返回一个包裹的 Promise,带来微小的性能开销。更重要的是,这种做法暗示其他开发者“此函数包含异步操作”,造成阅读歧义。

结语:规范从 Code Review 开始

这些看似细微的错误,在大型项目中可能引发严重的性能问题或难以调试的 bug。资深开发者建议团队建立以下规范:强制使用 Promise.all 并行无依赖任务;统一错误处理模式;在 Code Review 中重点检查 await 的使用场景。避开这些“坑”,才能真正释放 async/await 的优雅潜力,让异步代码既简洁又健壮。