JavaScript Promise完全指南
小白也能理解的异步编程核心知识点
什么是Promise?
Promise 就像是你在快餐店点餐时拿到的小票:
- 当你点餐时,服务员给你一张小票(创建一个Promise)
- 这张小票代表餐厅对你的承诺(Promise),会在未来某个时刻给你食物
- 食物可能成功准备好(fulfilled),也可能因为食材用完失败(rejected)
- 拿到食物后,小票就完成了它的使命(settled)
💡 简单说:Promise是表示异步操作最终完成或失败的对象
// 创建一个Promise
const myPromise = new Promise((resolve, reject) => {
// 异步操作,比如从服务器获取数据
// 成功时调用resolve(结果)
// 失败时调用reject(错误原因)
});
const myPromise = new Promise((resolve, reject) => {
// 异步操作,比如从服务器获取数据
// 成功时调用resolve(结果)
// 失败时调用reject(错误原因)
});
为什么需要Promise?
在Promise出现之前,我们使用回调函数处理异步操作:
回调地狱 😱
getUser(userId, function(user) {
getPosts(user.id, function(posts) {
getComments(posts[0].id, function(comments) {
// 更多嵌套…
});
});
});
getPosts(user.id, function(posts) {
getComments(posts[0].id, function(comments) {
// 更多嵌套…
});
});
});
问题:代码难以阅读和维护(俗称”回调地狱”)
Promise解决方案 ✨
getUser(userId)
.then(user => getPosts(user.id))
.then(posts => getComments(posts[0].id))
.then(comments => {
// 处理评论
})
.catch(error => console.error(error));
.then(user => getPosts(user.id))
.then(posts => getComments(posts[0].id))
.then(comments => {
// 处理评论
})
.catch(error => console.error(error));
优势:链式调用,代码更清晰,错误处理更简单
Promise的三种状态
每个Promise对象都有三种可能的状态:
Pending
(等待中)
(等待中)
→
Fulfilled
(已成功)
(已成功)
或
Rejected
(已失败)
(已失败)
- Pending(待定):初始状态,操作尚未完成
- Fulfilled(已兑现):操作成功完成
- Rejected(已拒绝):操作失败
⚠️ 重点:状态一旦改变就不会再变(不可逆)
🌰 实例:
const promise = new Promise((resolve, reject) => {
// 初始状态是 pending
// 5秒后状态变为 fulfilled
setTimeout(() => resolve(“成功啦!”), 5000);
// 或者如果发生错误:
// reject(new Error(“出问题啦!”));
});
// 初始状态是 pending
// 5秒后状态变为 fulfilled
setTimeout(() => resolve(“成功啦!”), 5000);
// 或者如果发生错误:
// reject(new Error(“出问题啦!”));
});
使用Promise:then/catch/finally
then() – 处理完成状态
当Promise成功时执行
promise.then(
result => { /* 处理成功结果 */ },
error => { /* 处理错误(可选) */ }
);
result => { /* 处理成功结果 */ },
error => { /* 处理错误(可选) */ }
);
catch() – 处理失败状态
当Promise失败时执行(专门处理错误)
promise.catch(
error => { /* 处理错误 */ }
);
error => { /* 处理错误 */ }
);
finally() – 无论成功失败都执行
清理工作(如关闭加载动画)
promise.finally(() => {
// 无论成功失败都会执行
// 适合做清理工作
});
// 无论成功失败都会执行
// 适合做清理工作
});
💡 最佳实践:使用链式调用
fetchData()
.then(data => processData(data))
.then(result => displayResult(result))
.catch(error => showError(error))
.finally(() => hideLoader());
.then(data => processData(data))
.then(result => displayResult(result))
.catch(error => showError(error))
.finally(() => hideLoader());
Promise链式调用
Promise最强大的功能之一:可以将多个异步操作链接在一起
// 模拟获取用户数据
function getUser(id) {
return new Promise(resolve => {
setTimeout(() => resolve({ id, name: ‘小明’ }), 1000);
});
}
// 模拟获取用户订单
function getOrders(userId) {
return new Promise(resolve => {
setTimeout(() => resolve([‘订单1’, ‘订单2’]), 800);
});
}
// 链式调用
getUser(123)
.then(user => {
console.log(‘用户:’, user.name);
return getOrders(user.id); // 返回新的Promise
})
.then(orders => {
console.log(‘订单:’, orders);
})
.catch(error => {
console.error(‘出错:’, error);
});
function getUser(id) {
return new Promise(resolve => {
setTimeout(() => resolve({ id, name: ‘小明’ }), 1000);
});
}
// 模拟获取用户订单
function getOrders(userId) {
return new Promise(resolve => {
setTimeout(() => resolve([‘订单1’, ‘订单2’]), 800);
});
}
// 链式调用
getUser(123)
.then(user => {
console.log(‘用户:’, user.name);
return getOrders(user.id); // 返回新的Promise
})
.then(orders => {
console.log(‘订单:’, orders);
})
.catch(error => {
console.error(‘出错:’, error);
});
💡 链式调用的关键在于:每个then()方法都返回一个新的Promise
Promise静态方法
Promise.all() – 所有成功才算成功
等待所有Promise完成,如果全部成功则返回结果数组,如果有一个失败则立即失败
Promise.all([
promise1,
promise2,
promise3
])
.then(results => {
// results = [result1, result2, result3]
})
.catch(error => {
// 任何一个Promise失败就会到这里
});
promise1,
promise2,
promise3
])
.then(results => {
// results = [result1, result2, result3]
})
.catch(error => {
// 任何一个Promise失败就会到这里
});
Promise.race() – 谁先完成用谁
多个Promise竞赛,哪个先完成(无论成功失败)就用哪个的结果
Promise.race([
fastPromise,
slowPromise
])
.then(firstResult => {
// 使用最先完成的结果
});
fastPromise,
slowPromise
])
.then(firstResult => {
// 使用最先完成的结果
});
Promise.allSettled() – 等所有都完成
等待所有Promise完成(无论成功失败),返回每个Promise的状态和结果
Promise.any() – 只要有1个成功
多个Promise中只要有一个成功就返回结果,全部失败才返回失败
🌰 实例:同时加载多个资源
// 同时加载用户数据和配置数据
Promise.all([
fetch(‘/api/user’),
fetch(‘/api/config’)
])
.then(([userData, configData]) => {
// 两个请求都完成才执行
initializeApp(userData, configData);
});
Promise.all([
fetch(‘/api/user’),
fetch(‘/api/config’)
])
.then(([userData, configData]) => {
// 两个请求都完成才执行
initializeApp(userData, configData);
});
常见错误与误区
误区1:忘记返回Promise
// 错误写法
somePromise
.then(result => {
anotherAsyncOperation(result); // 忘记return!
})
.then(newResult => {
// newResult是undefined!
});
somePromise
.then(result => {
anotherAsyncOperation(result); // 忘记return!
})
.then(newResult => {
// newResult是undefined!
});
// 正确写法
somePromise
.then(result => {
return anotherAsyncOperation(result); // 返回Promise
})
.then(newResult => {
// 正常获得结果
});
somePromise
.then(result => {
return anotherAsyncOperation(result); // 返回Promise
})
.then(newResult => {
// 正常获得结果
});
误区2:没有捕获错误
// 错误:未处理的Promise拒绝
asyncFunction().then(result => {
// 如果出错怎么办?
});
asyncFunction().then(result => {
// 如果出错怎么办?
});
// 正确:总是添加catch处理
asyncFunction()
.then(result => { … })
.catch(error => console.error(error));
asyncFunction()
.then(result => { … })
.catch(error => console.error(error));
误区3:Promise构造函数中使用try/catch
// 不推荐
new Promise((resolve, reject) => {
try {
// 异步操作
} catch (error) {
reject(error);
}
}); // 推荐:异步错误不会在try/catch中捕获
new Promise((resolve, reject) => {
// 直接处理错误
asyncOperation(param, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
new Promise((resolve, reject) => {
try {
// 异步操作
} catch (error) {
reject(error);
}
}); // 推荐:异步错误不会在try/catch中捕获
new Promise((resolve, reject) => {
// 直接处理错误
asyncOperation(param, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});