在现代 JavaScript 开发中,异步编程是不可避免的重要部分。无论是处理网络请求、读取文件,还是与数据库交互,我们都离不开异步操作。而 Promise,作为 ES6 引入的异步解决方案之一,在实际开发中发挥着核心作用。本文将深入探讨 JavaScript 中的 Promise API,帮助读者全面理解其工作原理、常用方法及应用场景。
一、Promise 是什么?
Promise 是 JavaScript 中用于处理异步操作的一种对象。它代表了某个异步操作最终可能完成(fulfilled)或失败(rejected)的一种值。
1.1 Promise 的定义
js
const promise = new Promise((resolve, reject) => {
// 异步操作
if (/* 成功 */) {
resolve(value); // 成功时调用
} else {
reject(error); // 失败时调用
}
});
1.2 Promise 的三种状态
Promise 有三种状态:
-
pending
(进行中):初始状态,既没有被 fulfilled,也没有被 rejected。 -
fulfilled
(已成功):操作成功完成。 -
rejected
(已失败):操作失败。
一旦状态改变,就不可再更改,即 Promise 是不可逆的。
二、Promise 的基本用法
2.1 then()
方法
用于注册成功和失败的回调函数。
js
promise
.then((value) => {
console.log("成功:", value);
})
.catch((error) => {
console.error("失败:", error);
});
2.2 catch()
方法
是 then(null, rejection) 的语法糖,用于捕获异常。
js
promise.catch((error) => {
console.error("发生错误:", error);
});
2.3 finally()
方法
不论 Promise 最终是成功还是失败,finally() 中的回调都会执行。
js
promise
.then((value) => {
console.log("成功", value);
})
.catch((error) => {
console.error("失败", error);
})
.finally(() => {
console.log("无论成功或失败都执行");
});
三、Promise 链式调用
Promise 最大的优点之一就是链式调用。你可以通过连续的 then() 方法来处理多个异步操作。
js
fetchData()
.then((data) => processData(data))
.then((result) => renderData(result))
.catch((error) => console.error(error));
这样写的好处是避免了"回调地狱"(callback hell)
,代码结构更清晰。
四、Promise 的静态方法
4.1 Promise.resolve()
返回一个状态为 fulfilled 的 Promise 对象。
js
Promise.resolve("ok").then((res) => console.log(res)); // 输出 "ok"
4.2 Promise.reject()
返回一个状态为 rejected 的 Promise 对象。
js
Promise.reject("error").catch((err) => console.error(err)); // 输出 "error"
4.3 Promise.all()
并发执行多个 Promise,并在全部成功后返回结果数组。
js
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then((values) => {
console.log(values); // [1, 2, 3]
});
⚠️ 注意:只要有一个 Promise 失败,整个 Promise.all 就会失败。
4.4 Promise.race()
多个 Promise 中只要有一个改变状态(无论成功或失败),就立即返回该结果。
js
Promise.race([
new Promise((res) => setTimeout(() => res("快的"), 100)),
new Promise((res) => setTimeout(() => res("慢的"), 200)),
]).then((res) => {
console.log(res); // 输出 "快的"
});
4.5 Promise.allSettled()
与 Promise.all() 类似,但无论失败与否,都能返回每个 Promise 的最终状态。
js
Promise.allSettled([
Promise.resolve(1),
Promise.reject("失败"),
]).then((results) => {
console.log(results);
// [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: '失败' }
// ]
});
4.6 Promise.any()
只要其中一个 Promise 成功,就返回成功的结果。若全部失败,则返回 AggregateError。
js
Promise.any([
Promise.reject("错误1"),
Promise.resolve("成功的"),
Promise.reject("错误2"),
]).then((value) => {
console.log(value); // 成功的
});
五、实现一个简单的 Promise
为了深入理解 Promise 的原理,我们来模拟一个简单的 Promise 实现:
js
class MyPromise {
constructor(executor) {
this.status = "pending";
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === "pending") {
this.status = "fulfilled";
this.value = value;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
if (this.status === "pending") {
this.status = "rejected";
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
if (this.status === "fulfilled") {
onFulfilled(this.value);
}
if (this.status === "rejected") {
onRejected(this.reason);
}
if (this.status === "pending") {
this.onResolvedCallbacks.push(() => onFulfilled(this.value));
this.onRejectedCallbacks.push(() => onRejected(this.reason));
}
}
}
六、Promise 与 async/await
ES2017 引入了 async/await,它是基于 Promise 的语法糖,使异步代码更像同步代码,易于编写和维护。
async function fetchData() {
try {
const res = await fetch("https://api.example.com");
const data = await res.json();
console.log(data);
} catch (err) {
console.error(err);
}
}
await 后面的表达式必须是一个 Promise。async 函数会自动返回一个 Promise。
七、Promise 的异常处理机制
Promise 具有链式的异常传递机制。如果 then 中抛出了错误,后续的 catch 会捕获它。
js
Promise.resolve()
.then(() => {
throw new Error("出错了");
})
.catch((err) => {
console.error("捕获异常:", err.message); // 捕获异常:出错了
});
此外,如果在 catch 之后继续写 then,Promise 会恢复为成功状态。
js
Promise.reject("失败")
.catch((err) => {
console.log("处理失败:", err);
return "默认值";
})
.then((res) => {
console.log("恢复执行:", res); // 恢复执行:默认值
});
八、实际开发中的 Promise 应用
8.1 接口请求并发处理
js
async function loadAllData() {
const [userInfo, articles, comments] = await Promise.all([
fetchUserInfo(),
fetchArticles(),
fetchComments(),
]);
console.log(userInfo, articles, comments);
}
8.2 防抖节流后的 Promise 延时
js
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function run() {
console.log("开始");
await delay(1000);
console.log("1 秒后执行");
}
九、常见错误与注意事项
-
then 中不返回新的 Promise:容易造成逻辑中断。
-
未处理 catch 异常:会导致 Promise "吞掉"异常。
-
在循环中使用 Promise:要注意执行顺序和并发控制。
-
Promise 状态不可逆:resolve/reject 只能调用一次。
十、总结
-
Promise 是处理异步操作的强大工具,具有链式调用、错误捕获、并发处理等优势。
-
它有三种状态:pending、fulfilled 和 rejected,一旦状态确定,就不可再更改。
-
提供多个静态方法如 Promise.all, Promise.race, Promise.allSettled 等,满足不同需求。
-
async/await 是基于 Promise 的语法糖,更加简洁优雅。
-
理解 Promise 的机制和链式调用,有助于编写更健壮、更清晰的异步代码。
如果你在实际项目中深入使用 Promise,一定会体会到它为异步编程带来的清晰结构与可维护性。在未来的开发中,配合 async/await,将进一步提升你的开发效率和代码质量。
欢迎关注我的公众号获取更多技术干货!