一、引言
为什么需要手写 Promise:
在现代前端开发中,异步编程是不可或缺的一部分,而Promise作为处理异步操作的核心工具,为我们提供了一种更优雅、更高效的方式来管理复杂的异步逻辑。通过手写实现一个简化版的Promise,我们能够更深入地理解其内部机制,从而更好地掌握异步编程的核心思想。以下是手写Promise的几个意义:
1. 深入理解异步编程机制:
- 通过手写
Promise,我们可以更清晰地理解异步任务的调度和执行机制,从而更好地理解事件循环、任务队列等底层概念。
2. 掌握 Promise 核心原理:
Promise的核心在于状态管理、链式调用和错误处理。- 通过手写实现,我们可以深入理解
Promise的三种状态(pending、fulfilled、rejected)是如何转换的,以及如何通过then方法实现链式调用和错误冒泡。
3. 为学习更复杂的异步方案打下基础:
- 理解
Promise是学习async/await和Generator的基础。
目标:
在本篇文章中,我们将实现一个简化版的Promise,目标是:
1. 符合 Promises/A+ 基本规范:
- 支持
Promise的三种状态(pending、fulfilled、rejected)。 - 实现
then方法,支持链式调用和异步执行。
2. 支持链式调用:
- 通过
then方法实现多个异步任务的顺序执行。
3. 支持异步执行:
- 确保
Promise的异步任务能够正确地放入任务队列中执行,而不是同步执行。
4. 错误处理:
- 实现
catch方法,支持错误捕获和冒泡。
二、Promise 的基本概念
1. Promise 三种状态
pending:初始状态,表示异步操作尚未完成。fulfilled:异步操作已兑现。rejected:异步操作已拒绝。- 状态只能从
pending变为fulfilled或rejected,且不可逆。
2. Promise 的基本用法
promise.then:用于注册兑现或拒绝的回调。promise.catch:用于注册拒绝回调。promise.finally:用于注册Promise完成后的回调,无论兑现或拒绝。
const promise = new Promise((resolve, reject) => {
// 运行异步代码
})
promise.then(
(value) => { /* 处理兑现结果 */ },
(reason) => { /* 处理拒绝结果 */ }
)
promise.catch(
(reason) => { /* 处理拒绝结果 */ },
)
promise.finally(
() => { /* 处理操作完成 */ }
)
3. Promise 的链式调用
Promise支持链式调用,我们可以将.then、.catch、.finally依次串联起来。
const promise = new Promise((resolve, reject) => {
// 运行异步代码
});
promise
.then(handleFulfilledA, handleRejectedA)
.then(handleFulfilledB)
.then(handleFulfilledC, handleRejectedC)
.catch(handleRejectedAny)
.finally(handleResolved)
4. Promise 常用的静态方法
promise.resolve:创建一个已兑现的Promise。promise.reject:创建一个已拒绝的Promise。promise.race:返回第一个完成(无论兑现或拒绝)的Promise的结果。promise.all:处理多个Promise,返回所有Promise的结果。如果所有Promise都兑现,返回一个包含所有兑现结果的数组;如果任一Promise拒绝,则整体拒绝,并返回第一个拒绝的原因。
三、实现一个简单的 Promise
1. 创建一个基础的 Promise 类
存储状态和值
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class SimplePromise {
constructor() {
this.state = PENDING; // 初始状态
this.value = null; // 兑现值或拒绝原因
this.handlers = []; // 回调队列
}
}
2. 实现resolve和reject方法
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class SimplePromise {
constructor(executor) {
this.state = PENDING; // 初始状态
this.value = null; // 兑现值或拒绝原因
this.handlers = []; // 回调队列
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch(error) {
this.reject(error);
}
}
resolve(value) {
this.setValue(FULFILLED, value);
}
reject(reason) {
this.setValue(REJECTED, reason);
}
setValue(state, value) {
if (this.state !== PENDING) {
return;
}
this.state = state; // 变更状态
this.value = value; // 存储兑现值或拒绝原因
this.handlers.forEach(handler => this.handle(handler)); // 执行回调
}
handle(handler) {
// ...
}
}
3. 实现then方法
(1) 初步实现then方法
我们先实现一个简单的then方法,用于注册回调函数。
then(onFulfilled, onRejected) {
this.handle({
onFulfilled,
onRejected,
});
}
handle(handler) {
if (this.state === PENDING) {
this.handlers.push(handler); // 状态未确定,加入回调队列
} else {
const callback = this.state === FULFILLED ? handler.onFulfilled : handler.onRejected;
if (callback) {
callback(this.value); // 执行回调
}
}
}
(2)实现 Promise 链式调用
如果要实现Promise的链式调用,then方法需要返回一个Promise实例。
如果then方法返回当前Promise实例,value的值将永远是第一个Promise的value,无法实现链式调用的值传递。
因此每次then方法都需要返回一个新的Promise实例,并在handle方法里处理回调函数的返回值,将其传递给新的Promise实例。
then(onFulfilled, onRejected) {
// 创建新 Promise 用于链式调用
return new SimplePromise((resolve, reject) => {
// 传递新 Promise 的 resolve 和 reject 到 handle 方法
// 以便根据回调结果改变新 Promise 状态
this.handle({
onFulfilled: typeof onFulfilled === 'function' ? onFulfilled : null,
onRejected: typeof onRejected === 'function' ? onRejected : null,
resolve,
reject,
});
});
}
handle(handler) {
if (this.state === PENDING) {
this.handlers.push(handler); // 状态未确定,加入回调队列
} else {
const callback = this.state === FULFILLED ? handler.onFulfilled : handler.onRejected;
const next = this.state === FULFILLED ? handler.resolve : handler.reject;
if (callback) {
try {
const result = callback(this.value); // 执行回调
handler.resolve(result); // 回调函数执行成功,将结果传递给下一个 Promise
} catch (error) {
handler.reject(error); // 如果回调函数执行过程中抛出异常,将异常传递给下一个 Promise
}
} else {
// 如果没有传递回调函数,将当前 Promise 的值传递给下一个 Promise
next(this.value);
}
}
}
(3)处理then方法返回 Promise 实例的情况
当then方法返回一个Promise实例时,我们需要等待该Promise完成,并根据其回调结果改变下一个Promise的状态。
then(onFulfilled, onRejected) {
// 创建新 Promise 用于链式调用
return new SimplePromise((resolve, reject) => {
// 传递新 Promise 的 resolve 和 reject 到 handle 方法
// 以便根据回调结果改变新 Promise 状态
this.handle({
onFulfilled: typeof onFulfilled === 'function' ? onFulfilled : null,
onRejected: typeof onRejected === 'function' ? onRejected : null,
resolve,
reject,
});
});
}
handle(handler) {
if (this.state === PENDING) {
this.handlers.push(handler); // 状态未确定,加入回调队列
} else {
const callback = this.state === FULFILLED ? handler.onFulfilled : handler.onRejected;
const next = this.state === FULFILLED ? handler.resolve : handler.reject;
if (callback) {
try {
const result = callback(this.value); // 执行回调
if (result instanceof SimplePromise) {
// 回调函数返回的 Promise
// 调用返回的 Promise 的 then 方法
// 并将下一个 Promise 的 resolve 和 reject 方法作为参数传入
// 以便在返回的 Promise 完成时,正确传递其结果
result.then(handler.resolve, handler.reject);
} else {
handler.resolve(result); // 回调函数返回普通值,直接传递给下一个 Promise
}
} catch (error) {
handler.reject(error); // 如果回调函数执行过程中抛出异常,将异常传递给下一个 Promise
}
} else {
// 如果没有传递回调函数,将当前 Promise 的值传递给下一个 Promise
next(this.value);
}
}
}
(4)处理resolve方法返回 Promise 实例的情况
如果resolve方法传入Promise,需要展平该Promise。即等待传入的Promise完成,并根据其结果决定当前Promise的状态
resolve(value) {
if (value instanceof SimplePromise) {
// 调用传入的 Promise 的 then 方法
// 并将当前 Promise 的 resolve 和 reject 方法作为参数传入
// 以便在传入的 Promise 完成时,正确传递其结果
value.then(this.resolve.bind(this), this.reject.bind(this));
return;
}
this.setValue(FULFILLED, value);
}
4. 模拟微任务的机制
当前的SimplePromise实现基本符合Promise的核心功能,但在事件循环和微任务的执行顺序上存在一些问题。
(1)事件循环与微任务
- 原生
Promise的回调函数会被放入微任务队列,在当前宏任务执行完毕后立即执行。 - 当前的
SimplePromise实现中,回调函数是同步执行的,没有模拟微任务的机制。导致回调函数的执行顺序与原生Promise不一致。
(2)resolve 方法中的递归调用
- 在
resolve方法中,如果传入的值是一个Promise实例,会递归调用then方法。这种实现没有考虑微任务的执行顺序。 - 如果递归调用过多,可能会导致栈溢出。
为了符合事件循环和微任务的执行顺序,我们需要对SimplePromise进行以下改进:
(1)模拟微任务队列
- 使用
queueMicrotask模拟微任务的执行顺序。 - 将回调函数放入微任务队列,确保它们在当前宏任务执行完毕后执行。
(2)优化 resolve 方法
- 在
resolve方法中,如果传入的值是一个Promise实例,使用微任务机制等待其完成,而不是直接递归调用。
以下是修改后的SimplePromise实现:
resolve和reject方法:
resolve(value) {
if (value instanceof SimplePromise) {
// 使用微任务队列等待传入的 Promise 完成
queueMicrotask(() => {
// 调用传入的 Promise 的 then 方法
// 并将当前 Promise 的 resolve 和 reject 方法作为参数传入
// 以便在传入的 Promise 完成时,正确传递其结果
value.then(this.resolve.bind(this), this.reject.bind(this));
});
return;
}
this.setValue(FULFILLED, value);
}
reject(reason) {
this.setValue(REJECTED, reason);
}
- 如果传入的值是一个
Promise实例,使用queueMicrotask等待其完成。 - 如果传入的值是普通值,使用
queueMicrotask确保状态变更和回调函数的异步执行。
handle方法
handle(handler) {
if (this.state === PENDING) {
this.handlers.push(handler); // 状态未确定,加入回调队列
} else {
// 使用微任务执行回调
queueMicrotask(() => {
const callback = this.state === FULFILLED ? handler.onFulfilled : handler.onRejected;
const next = this.state === FULFILLED ? handler.resolve : handler.reject;
if (callback) {
try {
const result = callback(this.value); // 执行回调
if (result instanceof SimplePromise) {
// 回调函数返回的 Promise
// 调用返回的 Promise 的 then 方法
// 并将下一个 Promise 的 resolve 和 reject 方法作为参数传入
// 以便在返回的 Promise 完成时,正确传递其结果
result.then(handler.resolve, handler.reject);
} else {
handler.resolve(result); // 回调函数返回普通值,直接传递给下一个 Promise
}
} catch (error) {
handler.reject(error); // 如果回调函数执行过程中抛出异常,将异常传递给下一个 Promise
}
} else {
// 如果没有传递回调函数,将当前 Promise 的值传递给下一个 Promise
next(this.value);
}
});
}
}
- 使用
queueMicrotask确保回调函数的异步执行。
四、功能扩展与优化
1. 添加catch方法
catch(onRejected) {
return this.then(null, onRejected);
}
2. 添加finally方法
finally方法在Promise完成(无论兑现或拒绝)时会执行。虽然第一反应是通过this.then(onFinally, onFinally)实现:
finally(onFinally) {
return this.then(onFinally, onFinally);
}
但这种实现方式存在一些问题,原因如下:
(1)回调函数不接收参数
原生的Promise.finally的回调函数不接收Promise的结果,它仅执行回调函数。
如果直接使用this.then(onFinally, onFinally),onFinally会接收到Promise的结果,这与规范不符。
Promise.resolve('success')
.finally(() => {
// 这里没有参数传入
console.log('finally executed');
})
.then(value => console.log(value));
// 错误实现
// 直接使用`this.then(onFinally, onFinally)`
SimplePromise.resolve('success')
.finally((result) => {
console.log(result); // 输出:success(不符合规范)
return result;
})
.then(value => console.log(value));
(2)不改变 Promise 的状态和值
原生的Promise.finally不会改变原Promise的状态和值。无论原Promise是兑现还是拒绝,finally执行后返回的Promise会保持原有的状态和值。
如果直接使用this.then(onFinally, onFinally),当onFinally返回一个值或抛出错误时,会改变原Promise的状态和值。
Promise.resolve('original value')
.finally(() => {
// 返回值不会影响原 Promise 的结果
return 'new value';
})
.then(value => console.log(value)); // 输出:original value
// 错误实现
// 直接使用`this.then(onFinally, onFinally)`
SimplePromise.resolve('original value')
.finally(() => {
// 返回值会影响原 Promise 的结果
return 'new value';
})
.then(value => console.log(value)); // 输出:new value
(3)处理 onFinally 返回 Promise 的情况
如果onFinally返回一个Promise,原生的Promise.finally会等待该Promise完成后再继续执行后续逻辑。
如果直接使用this.then(onFinally, onFinally),代码会直接继续执行,不会等待onFinally返回的Promise完成。
Promise.resolve('Original value')
.finally(() => {
return new Promise(resolve => {
setTimeout(() => {
console.log('onFinally Promise resolved');
resolve();
}, 1000);
});
}).then(value => console.log('finally value:', value));
// 输出:onFinally Promise resolved
// 输出:finally value: Original value
// 错误实现
// 直接使用`this.then(onFinally, onFinally)`
SimplePromise.resolve('Original value')
.finally(() => {
return new Promise(resolve => {
setTimeout(() => {
console.log('onFinally Promise resolved');
resolve();
}, 1000);
});
}).then(value => console.log('finally value:', value));
// 输出:finally value: Original value
// 输出:onFinally Promise resolved(顺序错误)
正确实现
为了符合规范,finally方法需要确保:
- 回调函数不接收参数。
- 不改变原
Promise的状态和值。 - 正确处理
onFinally返回的Promise。
以下是正确实现:
finally(onFinally) {
return this.then(
value => SimplePromise.resolve(onFinally()).then(() => value),
error => SimplePromise.resolve(onFinally()).then(() => { throw error; })
);
}
实现解析:
SimplePromise.resolve(onFinally()):确保onFinally的返回值被包装成Promise,并等待其完成.then(() => value):兑现时保留原Promise的值.then(() => { throw error; }):拒绝时抛出原Promise的错误
3. 实现静态方法resolve和reject
Promise.resolve和Promise.reject是Promise的两个重要静态方法,用于快速创建已兑现或已拒绝的Promise对象。以下是实现细节:
Promise.resolve的实现:
- 如果
Promise.resolve传入的值是一个Promise对象,则直接返回该Promise对象; - 如果
Promise.resolve传入的值是一个 thenable 对象,则调用其then方法,并返回一个已兑现的Promise对象; - 如果
Promise.resolve其他值,则返回一个已兑现的Promise对象,其兑现值就是传入的值;
static resolve(value) {
if (value instanceof SimplePromise) {
return value; // 直接返回 Promise
}
if (typeof value === 'object' && typeof value.then === 'function') {
return new SimplePromise((resolve, reject) => {
value.then(resolve, reject); // 处理 thenable 对象
});
}
return new SimplePromise((resolve) => resolve(value)); // 普通值
}
static reject(reason) {
return new SimplePromise((_, reject) => reject(reason));
}
4. 实现静态方法all和race
static all(promises) {
return new SimplePromise((resolve, reject) => {
let results = new Array(promises.length);
let completedCount = 0;
if (promises.length === 0) {
resolve(results);
return;
}
promises.forEach((promise, index) => {
SimplePromise.resolve(promise).then(
value => {
results[index] = value;
completedCount++;
if (completedCount === promises.length) {
resolve(results)
}
},
reject // 任一 Promise 拒绝,整体拒绝
);
});
})
}
static race(promises) {
return new SimplePromise((resolve, reject) => {
if (promises.length === 0) {
return;
}
promises.forEach(promise => {
SimplePromise.resolve(promise).then(resolve, reject); // 第一个完成的 Promise 决定结果
});
})
}
五、完整代码实现
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class SimplePromise {
static resolve(value) {
if (value instanceof SimplePromise) {
return value; // 直接返回 Promise
}
if (typeof value === 'object' && typeof value.then === 'function') {
return new SimplePromise((resolve, reject) => {
value.then(resolve, reject); // 处理 thenable 对象
});
}
return new SimplePromise((resolve) => resolve(value)); // 普通值
}
static reject(reason) {
return new SimplePromise((_, reject) => reject(reason));
}
static all(promises) {
return new SimplePromise((resolve, reject) => {
let results = new Array(promises.length);
let completedCount = 0;
if (promises.length === 0) {
resolve(results);
return;
}
promises.forEach((promise, index) => {
SimplePromise.resolve(promise).then(
value => {
results[index] = value;
completedCount++;
if (completedCount === promises.length) {
resolve(results)
}
},
reject // 任一 Promise 拒绝,整体拒绝
);
});
})
}
static race(promises) {
return new SimplePromise((resolve, reject) => {
if (promises.length === 0) {
return;
}
promises.forEach(promise => {
SimplePromise.resolve(promise).then(resolve, reject); // 第一个完成的 Promise 决定结果
});
})
}
constructor(executor) {
this.state = PENDING; // 初始状态
this.value = null; // 兑现值或拒绝原因
this.handlers = []; // 回调队列
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch(error) {
this.reject(error);
}
}
resolve(value) {
if (value instanceof SimplePromise) {
// 使用微任务等待传入的 Promise 完成
queueMicrotask(() => {
value.then(
val => this.resolve(val), // 兑现时传递结果
err => this.reject(err) // 拒绝时传递错误
);
});
return;
}
this.setValue(FULFILLED, value);
}
reject(reason) {
this.setValue(REJECTED, reason);
}
setValue(state, value) {
// 状态不可逆
if (this.state !== PENDING) {
return;
}
this.state = state; // 变更状态
this.value = value; // 存储兑现值或拒绝原因
this.handlers.forEach(handler => this.handle(handler)); // 执行回调
}
then(onFulfilled, onRejected) {
// 创建新 Promise 用于链式调用
return new SimplePromise((resolve, reject) => {
// 传递新 Promise 的 resolve 和 reject 到 handle 方法
// 以便根据回调结果改变新 Promise 状态
this.handle({
onFulfilled: typeof onFulfilled === 'function' ? onFulfilled : null,
onRejected: typeof onRejected === 'function' ? onRejected : null,
resolve,
reject,
});
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(onFinally) {
return this.then(
value => SimplePromise.resolve(onFinally()).then(() => value),
error => SimplePromise.resolve(onFinally()).then(() => { throw error; })
);
}
handle(handler) {
if (this.state === PENDING) {
this.handlers.push(handler); // 状态未确定,加入回调队列
} else {
// 使用微任务执行回调
queueMicrotask(() => {
const callback = this.state === FULFILLED ? handler.onFulfilled : handler.onRejected;
const next = this.state === FULFILLED ? handler.resolve : handler.reject;
if (callback) {
try {
const result = callback(this.value); // 执行回调
if (result instanceof SimplePromise) {
// 回调函数返回的 Promise
// 调用返回的 Promise 的 then 方法
// 并将下一个 Promise 的 resolve 和 reject 方法作为参数传入
// 以便在返回的 Promise 完成时,正确传递其结果
result.then(handler.resolve, handler.reject);
} else {
handler.resolve(result); // 回调函数返回普通值,直接传递给下一个 Promise
}
} catch (error) {
handler.reject(error); // 如果回调函数执行过程中抛出异常,将异常传递给下一个 Promise
}
} else {
// 如果没有传递回调函数,将当前 Promise 的值传递给下一个 Promise
next(this.value);
}
});
}
}
}
六、测试用例
1. 测试基本功能
const promise1 = new SimplePromise((resolve) => {
setTimeout(() => {
resolve('兑现')
}, 1000);
});
promise1.then(value => {
console.log(value); // 输出:兑现
});
2. 测试链式调用
const promise2 = new SimplePromise((resolve) => {
setTimeout(() => {
resolve(1)
}, 100);
});
promise2
.then(value => {
console.log(value); // 输出:1
return value + 1
})
.then(value => {
console.log(value); // 输出:2
});
3. 测试错误处理
const promise3 = new SimplePromise((resolve, reject) => {
setTimeout(() => {
reject('拒绝')
}, 1000);
});
promise3.catch(error => {
console.log(error); // 输出:拒绝
});
4. 测试finally方法
const promise4 = new SimplePromise((resolve) => {
setTimeout(() => {
resolve('finally test');
}, 100);
});
promise4
.finally(() => {
console.log('finally executed');
})
.then(value => {
console.log(value); // 输出:finally test
});
5. 测试Promise.resolve方法
const promise5 = SimplePromise.resolve(100);
promise5.then(value => {
console.log(value); // 输出:100
});
6. 测试Promise.reject方法
const promise6 = SimplePromise.reject(200);
promise6.catch(error => {
console.log(error); // 输出:200
});
7. 测试Promise.all方法
const promise7 = SimplePromise.resolve(1);
const promise8 = SimplePromise.resolve(2);
const promise9 = SimplePromise.resolve(3);
SimplePromise.all([promise7, promise8, promise9])
.then(values => {
console.log(values); // 输出:[1, 2, 3]
});
// 测试空数组
SimplePromise.all([])
.then(values => {
console.log(values); // 输出:[]
});
// 测试包含非Promise值
SimplePromise.all([1, 2, 3])
.then(values => {
console.log(values); // 输出:[1, 2, 3]
});
8. 测试Promise.race方法
const promise10 = new SimplePromise((resolve) => {
setTimeout(() => {
resolve('promise10');
}, 200);
});
const promise11 = new SimplePromise((resolve) => {
setTimeout(() => {
resolve('promise11');
}, 100);
});
SimplePromise.race([promise10, promise11])
.then(value => {
console.log(value); // 输出:promise11
});
// 测试空数组
SimplePromise.race([])
.then(value => {
console.log(value); // 不会输出,因为没有Promise完成
});
9. 测试链式调用中的错误处理
const promise12 = new SimplePromise((resolve, reject) => {
setTimeout(() => {
reject('error');
}, 100);
});
promise12
.then(value => {
console.log(value); // 不会执行
})
.catch(error => {
console.log(error); // 输出:error
return 'recovered';
})
.then(value => {
console.log(value); // 输出:recovered
});
10. 测试resolve方法传入Promise的情况
const promise13 = new SimplePromise((resolve) => {
setTimeout(() => {
resolve('promise13');
}, 100);
});
const promise14 = new SimplePromise((resolve) => {
setTimeout(() => {
resolve(promise13);
}, 100);
});
promise14.then(value => {
console.log(value); // 输出:promise13
});