一、Promise 基础知识
1. Promise 是什么?
-
定义:Promise 是 JavaScript 中用于处理异步操作的对象,通过链式调用避免回调地狱(Callback Hell)。
-
核心思想 :将异步操作的结果封装成一个对象,通过
.then()
和.catch()
处理成功或失败的结果。 -
状态:
- Pending(等待态) :初始状态,异步操作未完成。
- Fulfilled(成功态) :异步操作成功,返回结果。
- Rejected(失败态) :异步操作失败,返回错误。
2. 基础用法:创建和使用 Promise
示例 1:简单异步操作
typescript
// 创建一个 Promise,模拟异步操作(如网络请求)
const fetchData = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true; // 模拟成功或失败
if (success) {
resolve("数据加载成功!");
} else {
reject("数据加载失败!");
}
}, 1000);
});
// 使用 then 和 catch 处理结果
fetchData
.then(result => {
console.log("成功:", result); // 输出: 成功: 数据加载成功!
})
.catch(error => {
console.error("失败:", error);
});
解析:
new Promise(executor)
:创建一个 Promise,executor
是立即执行的函数,包含异步逻辑。resolve(value)
:异步操作成功时调用,将 Promise 状态从pending
改为fulfilled
。reject(error)
:异步操作失败时调用,将状态改为rejected
。.then()
:处理成功的结果。.catch()
:处理错误。
示例 2:链式调用处理多个步骤
javascript
// 第一步:获取用户数据
function getUserData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ id: 1, name: "Alice" });
}, 1000);
});
}
// 第二步:根据用户ID获取订单数据
function getUserOrders(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([{ orderId: 101, total: 99.99 }]);
}, 500);
});
}
// 链式调用
getUserData()
.then(user => {
console.log("用户信息:", user); // 输出: 用户信息: { id: 1, name: "Alice" }
return getUserOrders(user.id); // 返回新的 Promise
})
.then(orders => {
console.log("订单列表:", orders); // 输出: 订单列表: [{ orderId: 101, total: 99.99 }]
})
.catch(error => {
console.error("发生错误:", error);
});
解析:
- 链式调用 :每个
.then()
返回一个新的 Promise,允许连续执行异步操作。 - 中间值传递 :第一个
.then()
返回的值会作为第二个.then()
的参数。 - 错误传递 :链中的任何错误都会被
.catch()
捕获。
示例 3:错误处理与恢复
javascript
// 模拟一个会失败的异步操作
function fetchDataWithError() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject("网络错误!");
}, 1000);
});
}
// 错误处理与恢复
fetchDataWithError()
.then(data => {
console.log("成功:", data);
})
.catch(error => {
console.error("错误:", error); // 输出: 错误: 网络错误!
// 恢复逻辑:返回默认值
return { data: "默认数据" };
})
.then(data => {
console.log("恢复后的数据:", data); // 输出: 恢复后的数据: { data: "默认数据" }
});
解析:
.catch()
:捕获错误后,可以通过返回一个值继续链式调用。- 错误恢复 :在
.catch()
中返回一个新值或新 Promise,后续.then()
会接收到该值。
二、Promise 高级用法
示例 4:Promise.all()
并行执行多个请求
javascript
// 模拟三个异步请求
function request1() {
return new Promise(resolve => setTimeout(() => resolve("结果1"), 1000));
}
function request2() {
return new Promise(resolve => setTimeout(() => resolve("结果2"), 500));
}
function request3() {
return new Promise(resolve => setTimeout(() => resolve("结果3"), 800));
}
// 并行执行所有请求
Promise.all([request1(), request2(), request3()])
.then(results => {
console.log("所有请求成功:", results); // 输出: ["结果1", "结果2", "结果3"]
})
.catch(error => {
console.error("任一请求失败:", error);
});
解析:
Promise.all()
:接收一个 Promise 数组,所有成功时返回结果数组,任一失败则直接拒绝。- 并行执行:所有异步操作同时启动,但按顺序返回结果。
示例 5:Promise.race()
竞速模式
javascript
// 模拟两个异步请求
function fastRequest() {
return new Promise(resolve => setTimeout(() => resolve("快速请求"), 500));
}
function slowRequest() {
return new Promise(resolve => setTimeout(() => resolve("慢速请求"), 1000));
}
// 竞速:返回最快完成的请求
Promise.race([fastRequest(), slowRequest()])
.then(result => {
console.log("最快完成:", result); // 输出: 最快完成: 快速请求
})
.catch(error => {
console.error("最快失败:", error);
});
解析:
Promise.race()
:返回第一个完成的 Promise(无论成功或失败)。- 应用场景:适合需要"最先完成即成功"的场景(如超时控制)。
示例 6:设置异步操作超时
javascript
// 模拟一个可能超时的请求
function fetchWithTimeout(promise, timeout = 3000) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject("请求超时"), timeout)
)
]);
}
// 使用超时控制
fetchWithTimeout(
new Promise(resolve => setTimeout(() => resolve("数据"), 4000)),
2000
)
.then(data => {
console.log("成功:", data);
})
.catch(error => {
console.error("错误:", error); // 输出: 错误: 请求超时
});
解析:
Promise.race()
:与超时 Promise 竞速,若原请求未在指定时间内完成,则触发超时。- 应用场景:防止异步操作长时间阻塞用户界面。
三、Promise 与 Async/Await
1. 基本用法
- Async/Await 是 Promise 的语法糖,让异步代码更接近同步写法。
javascript
// 模拟异步操作
function fetchData() {
return new Promise(resolve => setTimeout(() => resolve("数据"), 1000));
}
// 使用 async/await
async function run() {
try {
const data = await fetchData();
console.log("成功:", data); // 输出: 成功: 数据
} catch (error) {
console.error("错误:", error);
}
}
run();
解析:
async
函数 :返回一个 Promise,内部可用await
暂停执行直到 Promise 完成。- 同步风格代码:异步逻辑看起来像同步代码,提高可读性。
2. 并发控制
Promise.all()
+await
:并行执行多个异步操作。
javascript
async function runAll() {
try {
const [result1, result2] = await Promise.all([
new Promise(resolve => setTimeout(() => resolve("A"), 1000)),
new Promise(resolve => setTimeout(() => resolve("B"), 500))
]);
console.log("结果:", result1, result2); // 输出: 结果: A B
} catch (error) {
console.error("错误:", error);
}
}
runAll();
3. 实际应用场景:表单提交与数据验证
javascript
// 模拟异步验证用户名是否已存在
function checkUsername(username) {
return new Promise(resolve => {
setTimeout(() => {
const exists = username === "admin";
resolve(!exists);
}, 500);
});
}
// 表单提交逻辑
async function submitForm(username, password) {
try {
if (username.length < 4) {
throw new Error("用户名至少4个字符");
}
const isAvailable = await checkUsername(username);
if (!isAvailable) {
throw new Error("用户名已存在");
}
if (password.length < 6) {
throw new Error("密码至少6个字符");
}
console.log("表单提交成功!");
} catch (error) {
console.error("表单提交失败:", error.message);
}
}
// 调用示例
submitForm("user123", "123456"); // 输出: 表单提交成功!
解析:
- 异步验证 :结合
async/await
和 Promise,实现表单字段的异步验证。 - 错误处理 :通过
try/catch
捕获并处理验证错误。
四. 手写 Promise 核心逻辑
简易版
ini
class MyPromise {
constructor(executor) {
this.state = "pending";
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.state === "pending") {
this.state = "fulfilled";
this.value = value;
}
};
const reject = (reason) => {
if (this.state === "pending") {
this.state = "rejected";
this.reason = reason;
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
if (this.state === "fulfilled") {
onFulfilled(this.value);
} else if (this.state === "rejected") {
onRejected(this.reason);
}
}
}
// 使用示例
new MyPromise((resolve, reject) => {
setTimeout(() => resolve("自定义Promise"), 1000);
})
.then(result => {
console.log(result); // 输出: 自定义Promise
})
.catch(error => {
console.error(error);
});
解析:
- 手写 Promise :模拟 Promise 的核心逻辑,包括状态管理、
resolve
/reject
调用和.then()
处理。 - 学习目标:理解 Promise 的底层原理,如状态不可逆、链式调用等。
完整版
ini
class MyPromise {
constructor(executor) {
this.state = "pending";
this.value = undefined;
this.reason = undefined;
// 存储成功/失败回调
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === "pending") {
this.state = "fulfilled";
this.value = value;
// 异步执行所有成功回调
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.state === "pending") {
this.state = "rejected";
this.reason = reason;
// 异步执行所有失败回调
this.onRejectedCallbacks.forEach(fn => fn());
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
// 默认值处理
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected = typeof onRejected === "function" ? onRejected : error => { throw error; };
// 返回新 Promise 支持链式调用
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === "fulfilled") {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.state === "rejected") {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.state === "pending") {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
}
// 处理 then 返回值与新 Promise 的关系
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
let called = false;
if (x instanceof MyPromise) {
if (x.state === "pending") {
x.then(value => {
resolvePromise(promise2, value, resolve, reject);
}, reject);
} else {
x.then(resolve, reject);
}
return;
}
if (x !== null && (typeof x === "object" || typeof x === "function")) {
try {
const then = x.then;
if (typeof then === "function") {
then.call(x, value => {
if (called) return;
called = true;
resolvePromise(promise2, value, resolve, reject);
}, error => {
if (called) return;
called = true;
reject(error);
});
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
修复后的使用示例
typescript
new MyPromise((resolve, reject) => {
setTimeout(() => resolve("自定义Promise"), 1000);
})
.then(result => {
console.log("成功:", result); // 输出: 成功: 自定义Promise
return "下一步"; // 返回新值
})
.then(result => {
console.log("下一步结果:", result); // 输出: 下一步结果: 下一步
})
.catch(error => {
console.error("失败:", error); // 现在可以正常调用 catch
});
修复点解析
1. 添加 catch
方法
-
问题 :原代码没有定义
catch
方法,导致.catch()
报错。 -
修复:
kotlinjavascript 深色版本 catch(onRejected) { return this.then(undefined, onRejected); }
catch
是then(null, onRejected)
的语法糖。
2. 链式调用支持
-
问题 :原
then
没有返回新 Promise,导致.then().catch()
无法链式调用。 -
修复:
then
返回一个新的MyPromise
实例。- 使用
resolvePromise
函数处理回调的返回值,确保能正确传递到下一个then
。
3. 异步回调处理
-
问题 :原代码未处理
pending
状态下的回调存储。 -
修复:
- 增加
onFulfilledCallbacks
和onRejectedCallbacks
数组,用于存储回调。 - 当
resolve
或reject
被调用时,异步执行这些回调。
- 增加
4. 处理异步操作
-
问题 :原代码未处理异步操作(如
setTimeout
)导致then
回调未触发。 -
修复:
- 在
then
中使用setTimeout
延迟执行回调,确保在事件循环中执行。 - 通过
resolvePromise
处理返回值(包括 Promise 对象)。
- 在
总结
通过上述示例,你可以掌握 Promise 的以下核心技能:
- 基础用法:创建和处理 Promise。
- 链式调用:连续执行异步操作。
- 错误处理:捕获和恢复错误。
- 静态方法 :
Promise.all
和Promise.race
的使用。 - 超时控制:结合竞速模式实现超时逻辑。
- Async/Await:简化异步代码。
- 实际应用:如表单验证、并行请求等。
- 底层原理:手写简化版 Promise。
这些技能将帮助你在工作中高效处理异步操作,并在面试中从容应对 Promise 相关问题。