1. 历史背景
在 JavaScript 的早期,异步操作主要通过回调函数(callback)实现。然而,随着应用变得更加复杂,嵌套的回调函数逐渐导致了 “回调地狱”(callback hell) 的问题。例如:
readFile('file1.txt', (err, data) => {
if (err) {
console.error(err);
} else {
processFile(data, (err, result) => {
if (err) {
console.error(err);
} else {
saveFile('file2.txt', result, (err) => {
if (err) console.error(err);
});
}
});
}
});
这种代码既难以阅读,又难以维护。为了缓解这一问题,ES6(2015年)引入了 Promise,一种用于更优雅地处理异步操作的机制。
2. 什么是 Promise?
Promise 是一个表示异步操作最终完成或失败的对象,其状态会经历以下三种状态之一:
- Pending(等待):初始状态,既没有完成也没有失败。
- Fulfilled(已完成):操作成功完成,Promise 变为 resolved 状态。
- Rejected(已拒绝):操作失败,Promise 变为 rejected 状态。
Promise 的状态一旦从 Pending 转变为 Fulfilled 或 Rejected,就不可再改变。
3. 基本使用
3.1 创建一个 Promise
Promise 构造函数接收一个执行器函数(executor),该函数包含两个参数 resolve
和 reject
。
const myPromise = new Promise((resolve, reject) => {
let success = true;
if (success) {
resolve("Operation succeeded!");
} else {
reject("Operation failed!");
}
});
3.2 使用 .then()
和 .catch()
myPromise
.then((result) => {
console.log(result); // 输出: Operation succeeded!
})
.catch((error) => {
console.error(error);
});
3.3 使用 .finally()
.finally()
方法在 Promise 结束后总会执行,无论是成功还是失败。
myPromise
.then((result) => console.log(result))
.catch((error) => console.error(error))
.finally(() => console.log("Operation finished."));
4. Promise 的链式调用
通过 .then()
链式调用,避免回调地狱:
const fetchData = (url) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`Data from ${url}`);
}, 1000);
});
};
fetchData('api/data1')
.then((data1) => {
console.log(data1);
return fetchData('api/data2');
})
.then((data2) => {
console.log(data2);
return fetchData('api/data3');
})
.then((data3) => {
console.log(data3);
})
.catch((error) => {
console.error("Error:", error);
});
5. 常用的 Promise 静态方法
5.1 Promise.all
用于等待多个 Promise 并行完成。如果任一 Promise 失败,则返回的 Promise 被拒绝。
Promise.all([
fetchData('api/data1'),
fetchData('api/data2'),
fetchData('api/data3')
])
.then((results) => {
console.log(results); // ['Data from api/data1', 'Data from api/data2', 'Data from api/data3']
})
.catch((error) => {
console.error("Error:", error);
});
5.2 Promise.race
用于返回第一个完成(无论成功或失败)的 Promise。
Promise.race([
fetchData('api/fast'),
fetchData('api/slow')
])
.then((result) => {
console.log(result); // 可能是 'Data from api/fast'
});
5.3 Promise.allSettled
即使部分 Promise 失败,也会等待所有 Promise 完成。
Promise.allSettled([
fetchData('api/data1'),
Promise.reject("Failed operation"),
])
.then((results) => {
console.log(results);
// [
// { status: "fulfilled", value: "Data from api/data1" },
// { status: "rejected", reason: "Failed operation" }
// ]
});
5.4 Promise.any
只要有一个 Promise 成功,就返回其结果;如果所有 Promise 都失败,则返回一个包含失败原因的 AggregateError。
Promise.any([
Promise.reject("Error 1"),
fetchData('api/data2'),
Promise.reject("Error 2"),
])
.then((result) => {
console.log(result); // "Data from api/data2"
})
.catch((error) => {
console.error(error);
});
6. 注意事项和最佳实践
6.1 错误处理
始终使用 .catch()
捕获错误,以避免未处理的 Rejection:
fetchData('invalid/url')
.then((result) => console.log(result))
.catch((error) => console.error("Error:", error));
6.2 避免嵌套 Promise
即使使用 Promise,也可能由于不当设计而引发嵌套问题:
// 不推荐
fetchData('api/data1').then((data1) => {
fetchData('api/data2').then((data2) => {
console.log(data1, data2);
});
});
// 推荐
fetchData('api/data1')
.then((data1) => {
return fetchData('api/data2').then((data2) => [data1, data2]);
})
.then(([data1, data2]) => {
console.log(data1, data2);
});
6.3 注意 Promise 状态不可逆
一旦状态从 Pending 变为 Fulfilled 或 Rejected,无法再次更改。这有助于避免状态冲突。
6.4 使用 async/await
简化代码
async/await
是 Promise 的语法糖,可以让异步代码看起来更像同步代码:
const fetchSequentialData = async () => {
try {
const data1 = await fetchData('api/data1');
console.log(data1);
const data2 = await fetchData('api/data2');
console.log(data2);
} catch (error) {
console.error("Error:", error);
}
};
fetchSequentialData();
7. 总结
Promise 改变了 JavaScript 的异步编程方式,提供了更清晰的语义、更优雅的代码结构和更强大的错误处理能力。在现代开发中,与 async/await
结合使用,Promise 成为构建高效、可维护异步逻辑的基石。