引言
在现代 JavaScript 开发中,Promise是处理异步操作的基石。从回调地狱道 async/await 的优雅,Promise 扮演了关键角色。但是,你真的理解 Promise 是如何工作的吗?
通过手写一个符合 Promise/A+ 规范的 Promise, 我们不仅能深入理解异步编程的机制,还能掌握 JavaScript 中许多核心概念: 微任务、事件循环、状态机等。更重要的是,这在面试中是必考的高频题目!
本文将带你从零实现一个完整的 Promise,并深入探讨其设计原理和实现细节。
一、Promise的核心概念
1.1 Promise的三种状态
javascript
// Promise的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
1.2 Promise/A+规范要点
Promise必须处于三种状态之一: pending、fulfilled、rejected- 状态一旦改变,就不能再次改变(不可逆性)
then方法必须返回一个新的Promisethen方法可以被同一个Promise调用多次- 值穿透(value penetration)
- 错误冒泡(error bubbling)
二、基础Promise的实现
2.1 基础骨架
javascript
class MyPromise {
constructor(executor) {
// 初始状态
this.state = "PENDING";
this.value = undefined;
this.reason = undefined;
// 用于存储 then 方法的回调函数
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
// 执行器函数
const resolve = (value) => {
if (this.state === "PENDING") {
this.state = "FULFILLED";
this.value = value;
// 执行所有成功回调
this.onFulfilledCallbacks.forEach((callback) => callback());
}
};
const reject = (reason) => {
if (this.state === "PENDING") {
this.state = "REJECTED";
this.reason = reason;
// 执行所有失败回调
this.onRejectedCallbacks.forEach((callback) => callback());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
// 基础实现,后续会完善
if (this.state === "FULFILLED") {
onFulfilled(this.value);
}
if (this.state === "REJECTED") {
onRejected(this.reason);
}
if (this.state === "PENDING") {
this.onFulfilledCallbacks.push(() => onFulfilled(this.value));
this.onRejectedCallbacks.push(() => onRejected(this.reason));
}
}
}
// 基础使用
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 1000);
});
promise.then(
(value) => console.log("成功:", value),
(reason) => console.log("失败:", reason)
);
// 成功: success
三、完整Promise实现(符合Promise/A+规范)
3.1 完整的MyPromise类
javascript
// 定义Promise的三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
// 判断是否为函数
const isFunction = (fn) => typeof fn === "function";
// 处理then方法中的回调,确保总是异步执行
const nextTick = (fn) => {
// 使用微任务(优先)或宏任务
if (typeof process !== "undefined" && process.nextTick) {
process.nextTick(fn); // NodeJS环境
} else if (typeof MutationObserver !== "undefined") {
// 浏览器环境使用 MutationObserver 模拟微任务
let counter = 1;
const observer = new MutationObserver(fn);
const textNode = document.createTextNode(String(counter));
observer.observe(textNode, { characterData: true });
counter = (counter + 1) % 2;
textNode.data = String(counter);
} else {
setTimeout(fn, 0); // 降级到宏任务
}
};
// 处理Promise解析过程(Promise Resolution Procedure)
function resolvePromise(promise, x, resolve, reject) {
// 防止循环引用
if (promise === x) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
// 标记是否已调用, 防止多次调用
let called = false;
if (x !== null && (typeof x === "object" || typeof x === "function")) {
try {
// 获取 then 方法
const then = x.then;
// 如果then是函数,则认为x是一个thenable对象
if (typeof then === "function") {
then.call(
x,
(y) => {
if (called) return;
called = true;
// 递归解析,直到得到非Promise值
resolvePromise(promise, x, resolve, reject);
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} else {
// 如果 then 不是函数,直接resolve x
resolve(x);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
// 如果x不是对象或函数, 直接resolve x
resolve(x);
}
}
class MyPromise {
constructor(executor) {
// 状态
this.state = PENDING;
// 成功值
this.value = undefined;
// 失败值
this.reason = undefined;
// 存储回调函数
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
// 定义resolve函数
const resolve = (value) => {
// 只有 pending 状态可以改变
if (this.state !== PENDING) return;
// 如果value是一个Promise, 则等待该Promise完成
if (value instanceof MyPromise) {
value.then(resolve, reject);
return;
}
// 异步执行
nextTick(() => {
if (this.state !== PENDING) return;
this.state = FULFILLED;
this.value = value;
// 执行所有成功回调
this.onFulfilledCallbacks.forEach((callback) => callback());
});
};
// 定义reject函数
const reject = (reason) => {
if (this.state !== PENDING) return;
nextTick(() => {
if (this.state !== PENDING) return;
this.state = REJECTED;
this.reason = reason;
// 执行所有失败回调
this.onRejectedCallbacks.forEach((callback) => callback());
});
};
// 执行executor
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// then方法
then(onFulfilled, onRejected) {
// 值穿透: 如果参数不是函数,则创建一个函数直接传递值
onFulfilled = isFunction(onFulfilled) ? onFulfilled : (value) => value;
onRejected = isFunction(onRejected)
? onRejected
: (reason) => {
throw reason;
};
// 创建一个新的Promise
const promise2 = new MyPromise((resolve, reject) => {
const handleFulfilled = () => {
nextTick(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
const handleRejected = () => {
nextTick(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
// 根据当前状态执行不同的逻辑
if (this.state === FULFILLED) {
handleFulfilled();
} else if (this.state === REJECTED) {
handleRejected();
} else {
// PENDING状态,将回调存储起来
this.onFulfilledCallbacks.push(handleFulfilled);
this.onRejectedCallbacks.push(handleRejected);
}
});
return promise2;
}
// catch方法
catch(onRejected) {
return this.then(null, onRejected);
}
// finally方法
finally(onFinally) {
return this.then(
(value) => MyPromise.resolve(onFinally()).then(() => value),
(reason) =>
MyPromise.resolve(onFinally()).then(() => {
throw reason;
})
);
}
// 静态方法: resolve
static resolve(value) {
// 如果已经是Promise实例,直接返回
if (value instanceof MyPromise) {
return value;
}
// 如果是thenable对象
if (value && typeof value.then === "function") {
return new MyPromise(value.then);
}
// 其他情况
return new MyPromise((resolve) => resolve(value));
}
// 静态方法: reject
static reject(reason) {
return new MyPromise((_, reject) => reject(reason));
}
// 静态方法: all
static all(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument must be an array"));
}
const results = [];
let completedCount = 0;
if (promises.length === 0) {
return resolve(results);
}
const processResult = (index, value) => {
results[index] = value;
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
};
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
(value) => processResult(index, value),
reject // 任何一个失败,整个Promise就失败
);
});
});
}
// 静态方法: race
static race(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument must be an array"));
}
promises.forEach((promise) => {
MyPromise.resolve(promise).then(resolve, reject);
});
});
}
// 静态方法: allSettled
static allSettled(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument must be an array"));
}
const results = [];
let completedCount = 0;
if (promises.length === 0) {
return resolve(results);
}
const processResult = (index, status, value) => {
results[index] = {
status,
[status === "fulfilled" ? "value" : "reason"]: value,
};
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
};
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
(value) => processResult(index, "fulfilled", value),
(reason) => processResult(index, "rejected", reason)
);
});
});
}
// 静态方法: any
static any(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument must be an array"));
}
const errors = [];
let rejectedCount = 0;
if (promises.length === 0) {
return reject(new AggregateError([], "All promises were rejected"));
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(resolve, (reason) => {
errors[index] = reason;
rejectedCount++;
if (rejectedCount === promises.length) {
reject(new AggregateError(errors, "All promises were rejected"));
}
});
});
});
}
}
四、测试用例
4.1 基础功能测试
javascript
console.log("=== 基础功能测试 ===");
// 测试1: 基础异步操作
const p1 = new MyPromise((resolve, reject) => {
setTimeout(() => resolve("success"), 100);
});
p1.then((value) => {
console.log("测试1:", value); // success
});
// 测试2: 链式调用
const p2 = new MyPromise((resolve) => resolve(1));
p2.then((value) => {
console.log("测试2-1:", value); // 1
return value + 1;
})
.then((value) => {
console.log("测试2-2:", value); // 2
return new MyPromise((resolve) => resolve(value + 1));
})
.then((value) => {
console.log("测试2-3:", value); // 3
});
// 测试3: 错误处理
const p3 = new MyPromise((resolve, reject) => {
reject("error");
});
p3.then(
(value) => console.log("不会执行"),
(reason) => console.log("测试3:", reason) // error
).catch((error) => {
console.log("测试3-catch:", error);
});
// 测试4: 值穿透
const p4 = new MyPromise((resolve) => resolve("value"));
p4.then()
.then()
.then((value) => console.log("测试4:", value)); // value
4.2 静态方法测试
javascript
console.log('\n=== 静态方法测试 ===');
// Promise.all
MyPromise.all([
MyPromise.resolve(1),
MyPromise.resolve(2),
new MyPromise(resolve => setTimeout(() => resolve(3), 100))
]).then(values => {
console.log('Promise.all:', values); // [1, 2, 3]
});
// Promise.race
MyPromise.race([
new MyPromise(resolve => setTimeout(() => resolve('fast'), 50)),
new MyPromise(resolve => setTimeout(() => resolve('slow'), 100))
]).then(value => {
console.log('Promise.race:', value); // fast
});
// Promise.allSettled
MyPromise.allSettled([
MyPromise.resolve(1),
MyPromise.reject('error'),
MyPromise.resolve(3)
]).then(results => {
console.log('Promise.allSettled:', results);
// [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: 'error' },
// { status: 'fulfilled', value: 3 }
// ]
});
4.3 符合Promise/A+规范的测试
javascript
console.log("\n=== Promise/A+ 规范测试 ===");
// 测试 then 方法被多次调用
const p = new MyPromise((resolve) => {
setTimeout(() => resolve("multi-call"), 50);
});
p.then((value) => console.log("第一次调用:", value));
p.then((value) => console.log("第二次调用:", value));
p.then((value) => console.log("第三次调用:", value));
// 测试 then 方法的异步特性
console.log("同步代码开始");
new MyPromise((resolve) => resolve("async")).then((value) => {
console.log("异步回调:", value);
});
console.log("同步代码结束");
// 输出顺序:
// 同步代码开始
// 同步代码结束
// 异步回调: async
// 测试循环引用
const p5 = new MyPromise((resolve) => resolve(1));
const p6 = p5.then(() => p6);
p6.then(
() => console.log("不会执行"),
(reason) => console.log("循环引用错误:", reason instanceof TypeError) // true
);
4.4 复杂场景测试
javascript
console.log("\n=== 复杂场景测试 ===");
// 场景1: 混合 Promise 和普通值
MyPromise.all([
1, // 普通值
MyPromise.resolve(2),
new MyPromise((resolve) => setTimeout(() => resolve(3), 100)),
]).then((values) => {
console.log("混合类型:", values); // [1, 2, 3]
});
// 场景2: 嵌套 Promise
const nested = new MyPromise((resolve) => {
resolve(
new MyPromise((innerResolve) => {
innerResolve("deep value");
})
);
});
nested.then((value) => {
console.log("嵌套 Promise:", value); // deep value
});
// 场景3: thenable 对象
const thenable = {
then: function (resolve, reject) {
setTimeout(() => resolve("thenable"), 50);
},
};
MyPromise.resolve(thenable).then((value) => {
console.log("Thenable 对象:", value); // thenable
});
// 场景4: finally 方法
new MyPromise((resolve) => resolve("finally test"))
.finally(() => {
console.log("finally 执行了");
return MyPromise.resolve("延迟");
})
.then((value) => {
console.log("finally 后的值:", value); // finally test
});
五、Promise的核心原理分析
5.1 状态机的实现
Promise本质上是一个状态机, 包含三种状态:
- pending: 初始状态
- fulfilled: 操作成功完成
- rejected: 操作失败
状态转换是不可逆的,这是通过内部状态变量和控制逻辑实现的。
5.2 异步调度的实现
原生的Promise使用微任务(microtask)队列, 而我们使用nextTick函数来模拟:
- NodeJS环境:
process.nextTick - 浏览器环境:
MutationObserver或setTimeout这种异步调度确保了:
then方法的回调总是在当前执行栈结束后执行- 符合
Promise/A+规范中的异步要求
5.3 链式调用的实现
链式调用的关键在于then方法总是返回一个新的Promise。这通过以下步骤实现:
- 创建新的Promise(
promise2) - 在适当的时机(状态改变时)执行回调
- 通过
resolvePromise处理回调的返回值
5.4 Promise解析过程
resolvePromise函数是Promise实现中最复杂的部分,它处理了:
- 循环引用检测
thenable对象的处理- 递归解析
- 防止多次调用
六、常见面试题实现
6.1 实现Promise.all
javascript
Promise.myAll = function (promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument must be an array"));
}
let results = [];
let count = 0;
if (promises.length === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
Promise.resolve(promise).then(
(value) => {
results[index] = value;
count++;
if (count === promises.length) {
resolve(results);
}
},
reject // 任何一个失败就整体失败
);
});
});
};
6.2 实现Promise.race
javascript
Promise.myRace = function (promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument must be an array"));
}
promises.forEach((promise) => {
Promise.resolve(promise).then(resolve, reject);
});
});
};
6.3 实现Promise超时控制
javascript
Promise.withTimeout = function (promise, timeout, timeoutMessage = "Timeout") {
return Promise.race([
promise,
new Promise((_, reject) => {
setTimeout(() => reject(new Error(timeoutMessage)), timeout);
}),
]);
};
// 使用示例
const fetchData = new Promise((resolve) => {
setTimeout(() => resolve("data"), 2000);
});
Promise.withTimeout(fetchData, 1000, "Request timeout")
.then((data) => console.log("成功:", data))
.catch((error) => console.log("失败:", error.message));
// 失败: Request timeout
6.4 实现Promise重试机制
javascript
Promise.retry = function (fn, times, delay = 1000) {
return new Promise((resolve, reject) => {
const attempt = (remaining) => {
fn()
.then(resolve)
.catch((error) => {
if (remaining <= 1) {
reject(error);
} else {
console.log(`重试,剩余次数: ${remaining - 1}`);
setTimeout(() => attempt(remaining - 1), delay);
}
});
};
attempt(times);
});
};
// 使用示例
let attemptCount = 0;
const unstableOperation = () => {
attemptCount++;
console.log(`第 ${attemptCount} 次尝试`);
return new Promise((resolve, reject) => {
if (Math.random() > 0.3) {
reject(new Error("随机失败"));
} else {
resolve("成功!");
}
});
};
Promise.retry(unstableOperation, 5, 500)
.then((result) => console.log("最终结果:", result))
.catch((error) => console.log("最终失败:", error.message));
七、性能优化和注意事项
7.1 内存泄漏预防
在Promise实现中要注意:
- 及时清理回调数组: 执行完回调后可以清空数组
- 避免循环引用: 在
resolvePromise中检测 - 使用
WeakMap存储元数据(可选)
7.2 错误处理最佳实践
javascript
// 不好的做法
promise.then(
successHandler,
errorHandler // 无法捕获 successHandler 中的错误
);
// 好的做法
promise
.then(successHandler)
.catch(errorHandler); // 可以捕获链中所有错误
// 或者在 then 中同时处理
promise.then(
value => {
try {
return successHandler(value);
} catch (error) {
return Promise.reject(error);
}
},
errorHandler
);
7.3 批量处理优化
javascript
// 优化大量 Promise 的处理
class PromiseBatch {
constructor(concurrency = 5) {
this.concurrency = concurrency;
this.queue = [];
this.running = 0;
}
add(promiseFactory) {
return new Promise((resolve, reject) => {
this.queue.push({
promiseFactory,
resolve,
reject
});
this.run();
});
}
run() {
while (this.running < this.concurrency && this.queue.length) {
const task = this.queue.shift();
this.running++;
task.promiseFactory()
.then(task.resolve)
.catch(task.reject)
.finally(() => {
this.running--;
this.run();
});
}
}
}
// 使用示例
const batch = new PromiseBatch(3);
const results = [];
for (let i = 0; i < 10; i++) {
batch.add(() => {
return new Promise(resolve => {
setTimeout(() => {
console.log(`任务 ${i} 完成`);
resolve(i);
}, Math.random() * 1000);
});
}).then(result => results.push(result));
}
八、现代JavaScript中的Promise相关特性
8.1 async/await的实现原理
async/await本质上是 Generator 函数和Promise的语法糖:
javascript
// async/await的近似实现
function asyncToGenerator(generatorFunc) {
return function (...args) {
const generator = generatorFunc.apply(this, args);
return new Promise((resolve, reject) => {
function step(key, arg) {
let result;
try {
result = generator[key](arg);
} catch (error) {
return reject(error);
}
const { value, done } = result;
if (done) {
return resolve(value);
} else {
return Promise.resolve(value).then(
(val) => step("next", val),
(err) => step("throw", err)
);
}
}
step("next");
});
};
}
// 使用示例
const asyncFunc = asyncToGenerator(function* () {
const result1 = yield Promise.resolve(1);
const result2 = yield Promise.resolve(result1 + 1);
return result2 + 1;
});
asyncFunc().then((value) => console.log(value)); // 3
8.2 Promise与生成器的配合
javascript
// 自动执行 Generator 函数
function runGenerator(genFunc) {
const generator = genFunc();
function handle(result) {
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value)
.then((res) => handle(generator.next(res)))
.catch((err) => handle(generator.throw(err)));
}
try {
return handle(generator.next());
} catch (error) {
return Promise.reject(error);
}
}
// 使用示例
runGenerator(function* () {
const a = yield Promise.resolve(1);
const b = yield Promise.resolve(2);
return a + b;
}).then((result) => console.log(result)); // 3
九、总结与最佳实践
9.1 核心要点总结
Promise是状态机: 三种状态,不可逆转换then方法返回新Promise: 实现链式调用的关键- 异步执行: 回调总是在当前执行栈结束后执行
- 错误冒泡: 错误会沿着
Promise链传递 - 值穿透: 非函数参数会被忽略
9.2 手写Promise的价值
- 深入理解异步机制: 理解微任务、事件循环
- 掌握设计模式: 状态机、观察者模式
- 提升代码质量: 更好的错误处理、代码组织
- 面试优势: 高频面试题,展示扎实的基础
9.3 实际开发建议
- 总是返回Promise: 确保函数的一致性
- 避免嵌套: 使用链式调用或
async/await - 处理所有错误: 使用
catch或try-catch - 合理使用静态方法:
all、race、allSettled等
9.4 进一步学习方向
- 源码阅读: 阅读原生
Promise的源码实现 - 异步编程模式: 学习
RxJS、async/await等 - 性能优化: 学习
Promise的性能优化技巧 - 框架集成: 学习如何在框架中合理使用
Promise
结语
通过手写Promise, 我们不仅掌握了一个重要的JavaScript特性,更重要的是理解了异步编程的核心思想。Promise的设计体现了许多优秀的软件设计原: 单一职责、开闭原则、依赖倒置等。
在实际开发中,我们可能不会经常需要自己实现Promise,但理解其原理能让我们更好地使用它,避免常见的陷阱,写出更健壮的异步代码。
记住,Promise不是终点,而是异步编程的起点。随着JavaScript的发展,我们有了async/await、Generator、Observable等更多强大的工具。但Promise作为基础,它的设计思想和实现原理永远值得我们深入学习和理解。
延伸阅读
希望这篇文章能帮助你深入理解Promise!如果有任何问题或建议,欢迎讨论交流。