JavaScript异步编程通过Promise和async/await避免阻塞主线程,二者本质相同但写法不同:Promise是带pending/fulfilled/rejected状态的对象,支持链式调用;async/await是其语法糖,使代码更同步化,需配合try/catch处理错误,且均不支持取消,须用AbortController实现请求中止。

JavaScript 异步编程不是“等一会儿再执行”,而是让代码在等待耗时操作(比如网络请求、文件读取)时不卡住主线程,继续响应用户或处理其他任务。Promise 和 async/await 是现代 JS 处理异步的两个核心机制,它们本质是同一套语义的不同写法,但后者更易读、更易错控。
Promise 是什么:一个表示异步操作最终完成或失败的对象
Promise 不是语法糖,而是一个有状态的容器:pending(进行中)、fulfilled(成功)、rejected(失败)。它把回调函数从“嵌套传入”变成“链式注册”,避免回调地狱。
常见错误现象:
- 忘记用
.catch()捕获错误,导致未处理的 rejection 被静默吞掉(尤其在 Node.js 中会触发unhandledRejection事件) - 在 Promise 构造函数里忘了调用
resolve或reject,导致状态永远卡在pending - 误以为
new Promise(() => { setTimeout(() => {}, 1000) })自动 resolve —— 实际上不调用resolve就永远不会结束
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 所有可能失败的异步操作,都应返回 Promise(如封装
fetch时主动处理response.ok) - 用
Promise.all([p1, p2])并行执行多个 Promise,但注意只要一个 reject 整体就失败;需要全部结果且容忍部分失败时,改用Promise.allSettled([p1, p2]) - 不要滥用
Promise.resolve(value)包裹同步值,除非你明确需要统一类型(比如函数签名要求返回 Promise)
async/await 是 Promise 的语法糖,但不是“更高级”的替代品
async 函数一定返回 Promise,await 只能在 async 函数内使用。它不能消除 Promise,只是让链式调用看起来像同步代码。
常见错误现象:
- 在顶层作用域(非 async 函数内)直接写
await fetch(...)→ 报错SyntaxError: await is only valid in async function - 用
await串行执行本可并行的任务(如连续 await 两个独立 API),白白拉长总耗时 - 忘记 try/catch 包裹
await表达式,导致错误冒泡到最近的catch或变成 unhandled rejection
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 想并行请求?先
const [a, b] = await Promise.all([fetchA(), fetchB()]),而不是分开 await - 错误处理优先用
try/catch,而非在每个await后加.catch();但若某一步失败不影响后续,可用await promise.catch(() => null) -
async () => {}是函数声明,async function foo() {}才是标准写法;箭头函数在 class 方法中可能丢失this,需留意
什么时候该用 Promise.then(),什么时候该用 async/await
没有绝对优劣,取决于场景和团队习惯。关键看控制流是否自然、错误是否集中处理、是否需要条件分支。
使用场景对比:
- 简单的一次性异步操作(如初始化加载)→
async/await更直白 - 需要在 Promise 链中做多次转换(如
.then(res => res.json()).then(data => data.items))→await写起来反而啰嗦,.then()更紧凑 - 动态构造异步流程(如根据前一步结果决定下一步 Promise)→
async/await的 if/else 更符合直觉 - 需要复用 Promise 实例(如缓存一个
const apiCall = fetch(...),后续多次.then())→ 必须用 Promise 原始形态,await会立即消费它
性能影响极小,V8 对两者做了深度优化,差异可忽略。但过度嵌套 await 在循环中(如 for-of 里 await)可能比 Promise.all + map 慢数倍。
真正容易被忽略的是:Promise 状态不可逆、不可取消;async/await 本身也不提供取消能力。如果需要中止请求,得配合 AbortController(如 fetch(url, { signal })),而不是指望 await 语句能“打断”。这和语言机制无关,是异步任务本身的约束。











