上一期我们见识了回调地狱的恐怖,今天终于迎来拯救者——Promise。
Promise 是 JavaScript 异步编程的真正转折点,它几乎完美解决了回调的三大痛点:
可读性差 → 错误处理麻烦 → 难以组合
1. Promise 到底是什么?
一句话概括:
Promise 是一个代表未来某个值的对象,它有三种状态,且状态一旦改变就不可逆转。
三种状态:
- pending(待定)→ 初始状态
- fulfilled(已兑现/成功)→ resolve(value)
- rejected(已拒绝/失败)→ reject(reason)
const p = new Promise((resolve, reject) => {
// 做一些异步操作...
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve("成功啦!数据是:666"); // 进入 fulfilled
} else {
reject(new Error("出错了!")); // 进入 rejected
}
}, 1500);
});
2. Promise 的核心用法:then / catch / finally
p
.then(value => {
console.log("成功:", value);
return "上一步的结果可以继续传下去"; // 可以链式
})
.then(nextValue => {
console.log("第二层:", nextValue);
})
.catch(err => {
console.error("任意一层出错都会到这里:", err);
})
.finally(() => {
console.log("不管成功失败我都会执行~");
});
最重要的特性:
- 链式调用:每个 then 都可以返回新值(或新 Promise),形成流畅的链
- 错误穿透:只要中间不被 catch 住,错误会一直向下传递到最后一个 catch
- 只有一个 catch 就够:不用像回调那样每层都写错误处理
3. Promise 静态方法(非常实用)
// 全部成功才成功,有一个失败就失败
Promise.all([p1, p2, p3])
.then(results => console.log("全部成功", results))
.catch(err => console.log("至少有一个失败了", err));
// 谁先完成用谁(常用于竞速、取最快响应)
Promise.race([p1, p2, p3])
.then(first => console.log("最快完成的是:", first));
// 全部结算完才结束(不管成功失败)ES2020+
Promise.allSettled([p1, p2, p3])
.then(results => {
// results 是 [{status:"fulfilled", value:xx}, {status:"rejected", reason:xx}] 的数组
});
// 只要有一个成功就成功(ES2023+)
Promise.any([p1, p2, p3])
.then(firstSuccess => console.log("至少有一个成功"))
.catch(() => console.log("全部都失败了"));
4. 真实业务场景对比:登录 → 获取信息 → 获取推荐
回调地狱版(上一期见过):
login(..., (err, token) => {
if (err) return;
getUserInfo(token, (err, user) => {
if (err) return;
getRecommendations(user.level, (err, products) => {
if (err) return;
render(products);
});
});
});
Promise 版(优雅太多):
login(username, password)
.then(token => getUserInfo(token))
.then(user => getRecommendations(user.level))
.then(products => renderProducts(products))
.catch(err => {
console.error("登录流程某一步失败:", err);
showErrorToast("出错了,请稍后重试");
});
5. Promise 常见陷阱(新手必看)
// 陷阱1:忘了 return 会断链
.then(() => {
doSomething(); // ← 没 return,下面拿不到结果
})
.then(result => { // ← result 是 undefined
// ...
});
// 陷阱2:Promise 里抛错不会自动 reject(除非在 executor 里)
new Promise((resolve) => {
throw new Error("同步错误"); // 这个错误不会被 catch 到!
});
// 正确做法:
new Promise((resolve, reject) => {
try {
// ...
} catch(e) {
reject(e);
}
});
6. 小结:Promise 带来的革命性改变
- 告别缩进地狱,代码从“向右发展”变成“向下流动”
- 统一的错误处理机制
- 强大的组合能力(all / race / allSettled / any)
- 成为现代异步的基石(几乎所有新 API 都返回 Promise)
可以说:学会 Promise = 掌握了现代 JavaScript 异步的 70%
下一期,我们将迎来目前最推荐的写法——async/await
它让异步代码看起来像同步代码一样清晰,同时保留了 Promise 的全部强大能力。
我们下期见~