一、JavaScript的单线程与事件循环
JavaScript是单线程语言——一个主线程处理所有任务。这避免了多线程编程中的竞争条件和死锁问题,但也意味着一个耗时操作会阻塞整个应用。异步编程就是在单线程环境下让耗时操作(如网络请求、文件I/O、定时器)不阻塞主线程的艺术。
理解异步编程的钥匙是事件循环(Event Loop)。执行流程:执行所有同步代码 → 清空微任务队列(Microtask Queue,包括Promise.then/catch/finally、MutationObserver)→ 执行一个宏任务(Macrotask,包括setTimeout/setInterval/I/O、UI Rendering)→ 再次清空微任务队列 → 循环往复。
二、三大异步范式演进
| 范式 | 引入版本 | 核心机制 | 典型问题 |
|---|---|---|---|
| Callback | 浏览器早期 | 函数作为参数传递,异步完成后调用 | 回调地狱、错误处理混乱 |
| Promise | ES6(2015) | 链式调用 .then().catch(),统一错误处理 | 长链可读性下降 |
| Async/Await | ES2017 | 同步风格写异步代码,try/catch错误处理 | 滥用串行await降低性能 |
三、Promise高级模式
- Promise.all():并行执行,全部成功才返回。任一失败则整体失败。适合互不依赖的并行任务
- Promise.allSettled():并行执行,等待全部完成(无论成败)。适合需要知道每个任务结果的场景,不会因个别失败而中断
- Promise.race():竞速,返回最先完成的结果(无论成败)。适合超时控制
- Promise.any():返回第一个成功的结果,全部失败才reject。适合多数据源容灾
四、Async/Await的陷阱与最佳实践
4.1 串行await陷阱
最常见的性能陷阱:
// ❌ 慢:串行执行(3秒)
const user = await fetchUser();
const posts = await fetchPosts();
const comments = await fetchComments();
// ✅ 快:并行执行(1秒)
const [user, posts, comments] = await Promise.all([
fetchUser(), fetchPosts(), fetchComments()
]);
4.2 错误处理
Async/Await的try/catch让错误处理回归直觉,但注意await在Promise创建时就开始了执行。推荐使用await-to-js模式(类似Go的错误处理):
const [err, data] = await to(fetchData());
if (err) return handleError(err);
4.3 循环中的异步
for...of + await是串行的;如需并行使用map + Promise.all。需要控制并发数时使用p-limit库或信号量模式。
五、2025年新趋势
- Top-Level Await(ES2022):模块顶层直接使用await,无需包装async函数
- Array.fromAsync()(ES2024):从异步可迭代对象创建数组
- Promise.withResolvers()(ES2024):更简洁的Promise创建方式
- Observable API(提案中):原生支持响应式数据流,替代部分RxJS场景
相关阅读:Go并发编程实战 | Python 3.13无GIL时代 | ES2025/2026新特性
评论 (57)