JavaScript异步编程应按场景选型:回调易致嵌套地狱且错误传递不统一;Promise支持链式调用但错误处理隐晦;async/await语法简洁但需注意try/catch、并发控制及返回值始终为Promise。

JavaScript 异步编程不是选“哪个更高级”,而是看场景和可维护性——回调函数能跑通,但容易陷入地狱;Promise 解决了嵌套,却让错误处理变隐晦;async/await 最接近同步写法,但不等于没有陷阱。
回调函数为什么容易出问题?
它本身不是错,错在多层嵌套 + 错误传递不统一。比如读文件后发请求再存数据库:
fs.readFile('a.json', (err, data) => {
if (err) throw err;
fetch('/api', { body: data })
.then(res => res.json())
.then(json => db.save(json))
.catch(err => console.error(err));
});
这段代码看似用了 Promise,但外层仍是回调——一旦 fetch 抛异常,catch 捕不到;如果 db.save 是回调风格,又得嵌一层,形成混合式混乱。
- 错误必须手动层层传,
if (err) return callback(err)容易漏写 - 无法用
try/catch统一捕获异步错误 -
return在回调里只退出当前函数,不影响外层流程
Promise 解决了什么,又带来了什么新问题?
Promise 的核心价值是「状态可组合」:pending → fulfilled/rejected,且 .then() 和 .catch() 总返回新 Promise,支持链式调用。
立即学习“Java免费学习笔记(深入)”;
但它对错误的处理有隐蔽性:
-
.then(success, fail)中的fail只捕获前一个Promise的 rejection,不捕获success回调里的异常 -
.catch()会吞掉未被处理的 rejection(尤其在未加catch的链尾) - 多个并行请求用
Promise.all([p1, p2])时,一个失败整个失败;想“全都要”得用Promise.allSettled
常见误写:
getUser().then(user => {
getPosts(user.id); // 忘了 return!结果后续 .then 接收到 undefined
}).then(posts => console.log(posts)); // posts 是 undefined
async/await 真的更简单吗?
语法糖没错,但掩盖了两个关键事实:
-
await只解包Promise的fulfilled值,rejected仍会抛出异常——所以必须配try/catch,不能靠.catch() -
await是顺序执行,想并发得显式用Promise.all包一层,否则性能反而更差 - 顶层
await只在模块作用域或async函数里合法,直接写await fetch(...)会报SyntaxError: await is only valid in async function
正确写法示例:
async function loadProfile() {
try {
const [user, posts] = await Promise.all([
fetch('/user').then(r => r.json()),
fetch('/posts').then(r => r.json())
]);
return { user, posts };
} catch (err) {
console.error('加载失败:', err.message);
throw err; // 不 throw,调用方拿不到错误
}
}
什么时候该用哪个?
没有银弹,但有现实约束:
- 老项目兼容 IE 或低版本 Node.js?只能用回调或
Promise(需 polyfill) - 需要精细控制错误传播路径(比如部分失败可降级)?
Promise的.catch()链比async/await的try/catch更灵活 - 团队新手多、逻辑分支深?
async/await可读性优势明显,但得盯紧try/catch是否覆盖所有await - 写库函数暴露 API?优先返回
Promise,而非async函数——后者返回的是带额外原型的Promise,且无法被Promise.resolve()正常包裹
最常被忽略的一点:async 函数内部哪怕没写 await,也一定会返回 Promise。这意味着 return 42 实际等价于 return Promise.resolve(42)——如果你依赖返回值是否为 Promise 做类型判断,这点必须意识到。











