自定义 Promise 实现
下面是一个使用 JavaScript 手写的比较完善的 Promise 实现,包含了所有常用的方法。这个实现涵盖了 Promise 的基本功能以及静态方法,如 resolve、reject、all、race、allSettled 和 any。
文件结构
假设我们将所有代码保存在 MyPromise.js 文件中。
MyPromise.js
class MyPromise {
// 构造函数
constructor(executor) {
this.state = 'pending'; // 状态:pending, fulfilled, rejected
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(value));
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn(reason));
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// then 方法
then(onFulfilled, onRejected) {
// 确保 onFulfilled 和 onRejected 是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
MyPromise.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
} else if (this.state === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.reason);
MyPromise.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
} else if (this.state === 'pending') {
this.onFulfilledCallbacks.push((value) => {
try {
const x = onFulfilled(value);
MyPromise.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
this.onRejectedCallbacks.push((reason) => {
try {
const x = onRejected(reason);
MyPromise.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
}
});
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) {
if (value instanceof MyPromise) {
return value;
}
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) => {
let count = 0;
const results = [];
const processData = (index, data) => {
results[index] = data;
count++;
if (count === promises.length) {
resolve(results);
}
};
for (let i = 0; i < promises.length; i++) {
MyPromise.resolve(promises[i]).then(
data => processData(i, data),
reason => reject(reason)
);
}
});
}
// 静态方法 race
static race(promises) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
MyPromise.resolve(promises[i]).then(resolve, reject);
}
});
}
// 静态方法 allSettled
static allSettled(promises) {
return new MyPromise((resolve) => {
let count = 0;
const results = [];
const processData = (index, data) => {
results[index] = data;
count++;
if (count === promises.length) {
resolve(results);
}
};
for (let i = 0; i < promises.length; i++) {
MyPromise.resolve(promises[i]).then(
value => processData(i, { status: 'fulfilled', value }),
reason => processData(i, { status: 'rejected', reason })
);
}
});
}
// 静态方法 any
static any(promises) {
return new MyPromise((resolve, reject) => {
let count = 0;
const errors = [];
for (let i = 0; i < promises.length; i++) {
MyPromise.resolve(promises[i]).then(
value => resolve(value),
reason => {
errors[i] = reason;
count++;
if (count === promises.length) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
}
);
}
});
}
// 解析 Promise
static resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
let called = false;
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
const then = x.then;
if (typeof then === 'function') {
then.call(
x,
y => {
if (called) return;
called = true;
MyPromise.resolvePromise(promise2, y, resolve, reject);
},
r => {
if (called) return;
called = true;
reject(r);
}
);
} else {
if (called) return;
called = true;
resolve(x);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
resolve(x);
}
}
}
// 导出模块(如果在Node环境中使用)
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = MyPromise;
}
使用示例
const MyPromise = require('./MyPromise');
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功');
}, 1000);
});
promise
.then(value => {
console.log(value); // 输出: 成功
return '继续链式调用';
})
.then(value => {
console.log(value); // 输出: 继续链式调用
throw new Error('出错了');
})
.catch(error => {
console.error(error); // 输出: Error: 出错了
})
.finally(() => {
console.log('Promise 结束'); // 输出: Promise 结束
});
说明
-
状态管理:
Promise有三种状态:pending(待定)、fulfilled(已兑现)和rejected(已拒绝)。状态一旦从pending变为fulfilled或rejected,便不可更改。 -
回调队列:当
Promise处于pending状态时,所有的回调(onFulfilled和onRejected)都会被存储在队列中,待状态改变后依次执行。 -
链式调用:
then方法会返回一个新的Promise,允许进行链式调用。通过resolvePromise方法处理返回值,确保链式调用的正确性。 -
静态方法:
resolve和reject:用于创建已解决或已拒绝的Promise。all:等待所有Promise完成,返回一个包含所有结果的Promise。race:竞争Promise,第一个完成的Promise的结果决定最终结果。allSettled:等待所有Promise完成,无论是成功还是失败,返回所有结果。any:返回第一个成功的Promise,如果全部失败则返回一个聚合错误。
-
错误处理:通过
try-catch块捕获执行器和回调中的错误,并正确地将Promise状态设为rejected。
这个实现遵循了 Promises/A+ 规范,确保了与原生Promise的兼容性。可以根据需要进一步扩展和优化。
resolvePromise 方法详解
在上述自定义 MyPromise 实现中,resolvePromise 方法是一个关键的辅助函数,用于处理 then 方法中返回的值,确保 Promise 链的正确性和遵循 Promises/A+ 规范。下面将详细解释 resolvePromise 方法的作用、工作原理以及为何如此设计。
作用
resolvePromise 的主要职责是:
-
解析回调返回值:在
then方法中,onFulfilled或onRejected回调可能会返回一个值x,这个值可能是普通值,也可能是一个Promise或类似Promise的对象(称为“thenable”)。resolvePromise确保x被正确解析,并决定promise2的状态及其最终值。 -
处理链式调用:通过递归解析
Promise链中的每一个步骤,确保最终结果的一致性和正确性,遵循 Promises/A+ 规范,避免出现循环引用等问题。
工作原理
resolvePromise 方法按照以下步骤工作:
1. 循环引用检测
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
- 目的:防止
promise2与x相互引用,导致无限递归和内存泄漏。 - 行为:如果
promise2和x指向同一个对象,立即拒绝promise2,并抛出TypeError。
2. 检查 x 是否为对象或函数
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
// 处理 thenable
} else {
resolve(x);
}
- 目的:仅当
x是对象或函数时,才有可能是 thenable,需要进一步解析;否则,直接将x作为promise2的成功值。
3. 获取 then 方法
const then = x.then;
- 目的:尝试从
x中获取then方法,以判断x是否为 thenable。
4. 检查 then 是否为函数
if (typeof then === 'function') {
// 处理 thenable
} else {
resolve(x);
}
- 目的:只有当
then是函数时,x才被视为 thenable;否则,将x作为普通对象处理。
5. 调用 then 方法
then.call(
x,
y => {
if (called) return;
called = true;
MyPromise.resolvePromise(promise2, y, resolve, reject);
},
r => {
if (called) return;
called = true;
reject(r);
}
);
- 目的:
- 绑定
this:使用call将this绑定为x,确保then方法在正确的上下文中执行。 - 处理成功回调
y:- 检查是否已经调用过(避免多次调用)。
- 递归调用
resolvePromise,以解析y,确保即使y也是 thenable,也能被正确解析。
- 处理失败回调
r:- 同样检查是否已经调用过。
- 直接拒绝
promise2,传递错误原因r。
- 绑定
6. 错误处理
try {
// 获取 then 并调用
} catch (error) {
if (called) return;
called = true;
reject(error);
}
- 目的:捕获在获取或调用
then方法过程中抛出的任何错误,并将promise2拒绝,传递该错误。
7. 防止多次调用
let called = false;
// 在每个回调中检查 called
if (called) return;
called = true;
- 目的:根据 Promises/A+ 规范,
then方法的resolve和reject回调只能被调用一次。使用called标志确保无论是成功还是失败回调,只能执行一次。
为什么需要 resolvePromise
根据 Promises/A+ 规范,then 方法必须能够处理各种返回值,包括普通值、Promise、以及其他 thenable 对象。resolvePromise 的设计确保:
-
链式调用的正确性:通过递归解析每一个返回值,确保整个
Promise链的状态和结果是正确的。 -
遵循规范:严格按照 Promises/A+ 规范处理各种情况,包括循环引用、thenable 解析、错误捕获等。
-
兼容性:确保自定义的
MyPromise实现与原生Promise行为一致,能够与各种现有代码和库无缝配合。
resolvePromise 方法在自定义 Promise 实现中扮演了至关重要的角色。它不仅负责解析 then 回调返回的值,还确保 Promise 链的稳定性和规范性。通过仔细处理各种可能的情况,包括 thenable 对象、错误捕获和循环引用检测,resolvePromise 确保了 MyPromise 实现的可靠性和一致性,使其能够与原生 Promise 行为相匹配。