本文最后更新于 47 天前,其中的信息可能已经有所发展或是发生改变。
一、为什么我们需要Promise?
1.1 回调地狱的噩梦
// 传统回调金字塔
getUser(id, function(user) {
getPosts(user.id, function(posts) {
getComments(posts.id, function(comments) {
renderUI(user, posts, comments);
}, errorHandler);
}, errorHandler);
}, errorHandler);
1.2 Promise的救赎
// Promise链式调用
getUser(id)
.then(user => getPosts(user.id))
.then(posts => getComments(posts.id))
.then(comments => renderUI(comments))
.catch(errorHandler);
1.3 核心优势对比
特性 | 回调函数 | Promise |
---|---|---|
可读性 | ❌ 嵌套地狱 | ✅ 链式调用 |
错误处理 | ❌ 分散处理 | ✅ 集中捕获 |
异步流程控制 | ❌ 手动实现 | ✅ 内置方法 |
状态管理 | ❌ 不可控 | ✅ 明确状态 |
二、Promise核心机制深度解析
2.1 三大状态生命周期
stateDiagram-v2
[*] --> pending
pending --> fulfilled: resolve()
pending --> rejected: reject()
fulfilled --> [*]
rejected --> [*]
2.2 创建Promise的三种姿势
// 1. 基础创建
const timerPromise = new Promise((resolve, reject) => {
setTimeout(() => resolve('Done!'), 1000);
});
// 2. 快捷方法
const success = Promise.resolve('Immediate');
const failure = Promise.reject(new Error('Oops!'));
// 3. 包装回调函数
const promisify = fn => (...args) =>
new Promise((resolve, reject) => {
fn(...args, (err, data) => {
err ? reject(err) : resolve(data);
});
});
三、高级用法实战手册
3.1 并发控制四剑客
// 1. 全部成功
Promise.all([fetchA(), fetchB()])
.then(([a, b]) => {/* 处理数据 */});
// 2. 竞速模式
Promise.race([timeout(500), fetchData()]);
// 3. 全量结算
Promise.allSettled([req1, req2])
.then(results => results.filter(r => r.status === 'fulfilled'));
// 4. 首个成功
Promise.any([fallbackAPI1(), fallbackAPI2()]);
3.2 错误处理进阶
// 1. 穿透捕获
fetchData()
.then(data => process(data))
.catch(err => {
console.error('请求失败:', err);
return getCacheData(); // 降级方案
});
// 2. 局部捕获
fetchData()
.then(data => {
return process(data).catch(e => {/* 处理步骤错误 */});
});
// 3. 全局监听
window.addEventListener('unhandledrejection', event => {
reportError(event.reason);
});
四、async/await革命性改进
4.1 同步写法异步逻辑
async function loadDashboard() {
try {
const user = await getUser();
const [posts, notifications] = await Promise.all([
getPosts(user.id),
getNotifications(user.id)
]);
return { user, posts, notifications };
} catch (error) {
await logError(error);
throw new Error('加载失败');
} finally {
hideLoading();
}
}
4.2 性能优化技巧
// 错误示范:顺序await
async function slowFetch() {
const a = await fetchA(); // 等待1秒
const b = await fetchB(); // 再等待1秒
return a + b; // 总耗时2秒
}
// 正确做法:并行执行
async function fastFetch() {
const [a, b] = await Promise.all([fetchA(), fetchB()]);
return a + b; // 总耗时1秒
}
五、十大高频问题解决方案
5.1 如何取消Promise?
function cancellablePromise(fn) {
let cancel;
const promise = new Promise((resolve, reject) => {
cancel = reject;
fn(resolve, reject);
});
return { promise, cancel };
}
// 使用示例
const { promise, cancel } = cancellablePromise(resolve => {
setTimeout(() => resolve('Data'), 2000);
});
cancelButton.addEventListener('click', () => cancel('用户取消'));
5.2 超时控制
function timeoutPromise(ms, promise) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`超时 ${ms}ms`)), ms)
)
]);
}
六、Promise最佳实践
- 避免嵌套陷阱
// ❌ 错误嵌套
promise.then(result => {
anotherPromise(result).then(/* ... */);
});
// ✅ 链式优化
promise
.then(result => anotherPromise(result))
.then(/* ... */);
- 异常穿透原则
fetchData()
.then(data => {
if (!data.valid) throw new Error('无效数据');
return process(data);
})
.catch(handleError); // 捕获所有异常
- 合理使用微任务
// 微任务执行顺序
Promise.resolve().then(() => console.log('微任务1'));
setTimeout(() => console.log('宏任务'), 0);
Promise.resolve().then(() => console.log('微任务2'));
// 输出顺序:
// 微任务1 → 微任务2 → 宏任务
🚀 升级你的异步代码
重构前(回调) | 重构后(Promise) |
---|---|
fs.readFile('a.txt', (err, data) => { ... }) |
fs.promises.readFile('a.txt').then(...) |
setTimeout(() => callback(), 1000) |
new Promise(r => setTimeout(r, 1000)).then(...) |
XMLHttpRequest |
fetch().then(res => res.json()) |
根据Chrome DevTools统计,合理使用Promise可以使异步代码执行效率提升40%
// 终极示例:实现Promise队列
class PromiseQueue {
constructor(concurrency = 1) {
this.queue = [];
this.running = 0;
this.concurrency = concurrency;
}
add(task) {
this.queue.push(task);
this.next();
}
next() {
while (this.running < this.concurrency && this.queue.length) {
const task = this.queue.shift();
task().finally(() => {
this.running--;
this.next();
});
this.running++;
}
}
}
// 使用示例
const queue = new PromiseQueue(2);
[1,2,3,4,5].forEach(i =>
queue.add(() =>
fetch(`/api/item/${i}`).then(/*...*/)
)
);