源码地址:github.com/Liangjiahon…
实现目标
- 实现一个
MyPromise
,包含原生 Promise
的功能和方法,并符合 Promises/A+
规范
- 其中
MyPromise
包括 executor(resolve,reject)
,实例方法 then
、catch
、finally
,类方法 resolve
,reject
,all
,allSettled
,race
,any
结构搭建
- 搭建
MyPromise
的基本结构,并且实现 executor
函数和状态管理
class MyPromise {
constructor(executor) {
this.status = 'pending';
this.value = undefined; // 调用reslove的值
this.reason = undefined;// 调用reject的值
// 立即执行executor
try {
executor(resolve, reject);
} catch (err) {
// 捕获executor中的错误,并执行reject
reject(err);
}
}
}
- 由于
executor
函数会立即执行,所以 constructor()
直接调用
- 使用
try...catch()
是需要捕获 executor
内部出错或抛出异常,捕获后并调用 rejected
// 如这种情况
new MyPromise((reslove, reject) => {
throw new Error()
})
resolve 和 reject
- 首先这两个函数是
Promise
的核心方法,用于异步操作处理的结果,并且在 resolve
或 reject
之后,Promise
的状态会发生对应改变,并且是不可逆的
class MyPromise {
constructor(executor) {
...
this.onFulfillFns = []; // 接收then方法中传入的成功回调
this.onRejectFns = []; // 接收then方法中传入的失败回调
const resolve = (value) => {
// 只有在pending状态才能改变Promise的状态
if (this.status === 'pending') {
this.status = 'fulfilled';
this.value = value;
// 这里执行收集到的成功处理函数
this.onFulfillFns.forEach((fn) => fn(value));
}
};
const reject = (reason) => {
if (this.status === 'pending') {
this.status = 'rejected';
this.reason = reason;
// 这里执行收集到的失败处理函数
this.onRejectFns.forEach((fn) => fn(reason));
}
};
...
}
- 由于
Promise
中会有异步操作,那么在 pending
状态时,当调用 promise
对象的 then
方法并传入回调,则需要先把回调保存起来,什么时候 resolve
或 reject
才会执行回调
// 如下所示
const promise = new MyPromise((resolve, reject) => {
settimeout(() => resolve(), 10000) // 10秒后resovle
})
// 这时候promise还是pending状态,其回调要在resolve或reject后才会执行
// 所以要将回调加入到数组中保存
promise.then((res) => {...})
promise.then((res) => {...})
promise.then((res) => {...})
promise.then((res) => {...})
实例方法
Promise
的实例方法有 then
、catch
、fianlly
,其中 then
方法才是符合 Promise/A+
规范的,catch
和 finally
是 ES6
的 Promise
实现的语法糖
then方法实现
then
方法用于为 Promise
实例添加成功和失败的处理回调,接收成功和失败的回调函数,并返回一个新的 Promise
,用于链式调用
then(onFulfilled, onRejected) {
return new MyPromise((reslove, reject) => { });
}
- 首先定义默认的成功和失败的回调函数,当调用
then
方法传入的不是函数时,赋予一个默认的函数
then(onFulfilled, onRejected) {
const defOnRejected = (reason) => { throw reason };
const defonFulfilled = (value) => value;
onFulfilled = isFunc(onFulfilled) ? onFulfilled : defonFulfilled;
onRejected = isFunc(onRejected) ? onRejected : defOnRejected;
return new MyPromise((reslove, reject) => { });
}
- 其次是要判断
promise
的状态,如果是 fulfilled
和 rejected
状态,直接将回调加入任务队列;如果是 pending
状态,则将回调存放到数组中
then(onFulfilled, onRejected) {
// 当不传入第二个回调时,将错误抛出给catch处理
const defOnRejected = (reason) => { throw reason };
const defonFulfilled = (value) => value;
onFulfilled = isFunc(onFulfilled) ? onFulfilled : defonFulfilled;
onRejected = isFunc(onRejected) ? onRejected : defOnRejected;
return new MyPromise((reslove, reject) => {
const isFulfilled = this.status === PROMISE_STATUS.FULFILLED;
const isRejected = this.status === PROMISE_STATUS.REJECTED;
const isPending = this.status === PROMISE_STATUS.PENDING;
// 如果在调用时,promise的状态已经确定,说明此时已经调用了resolve或reject
// 那么可以直接将回调加入任务队列等待执行
if (isFulfilled) {
queueMicrotask(() => execFnWithCatchErr(onFulfilled, this.value, reslove, reject));
}
if (isRejected) {
queueMicrotask(() => execFnWithCatchErr(onRejected, this.reason, reslove, reject));
}
// 如果是pending状态,那么需要将回调添加到数组保存
if (isPending) {
this.onFulfillFns.push((res) =>
queueMicrotask(() => execFnWithCatchErr(onFulfilled, res, reslove, reject))
);
this.onRejectFns.push((err) =>
queueMicrotask(() => execFnWithCatchErr(onRejected, err, reslove, reject))
);
} else {
this.onFulfillFns = [];
this.onRejectFns = [];
}
});
}
- 辅助函数
execFnWithCatchErr
实现如下
const isFunc = (fn) => Object.prototype.toString.call(fn) === '[object Function]';
const isObj = (o) => Object.prototype.toString.call(o) === '[object Object]';
// fn是 --- 成功或失败的回调
// value是 --- promise成功或失败后的结果
// resolve和reject是 --- 返回的新Promise的resolve和reject
const execFnWithCatchErr = (fn, value, resolve, reject) => {
try {
// 拿到then方法的返回值
const result = fn(value);
// 判断返回值是不是Promise, 或是带then方法的对象
if (result instanceof MyPromise || (isObj(result) && isFunc(result.then))) {
result.then(resolve, reject);
} else {
// 否则resolve出去
resolve(result);
}
} catch (err) {
// 如果在回调中抛出了异常,则直接捕获reject
reject(err);
}
};

catch方法实现
catch
方法只是语法糖,只接受一个回调作为参数,相当于 then
方法传入的第二个失败回调
catch(onRejected) {
return this.then(undefined, onRejected);
}
- 这里让成功的回调为
undefined
,调用 catch
方法相当于调用了只传入失败回调的 then
方法
finally方法实现
finally
方法也是语法糖,只接受一个回调作为参数,无论是 promise
成功或失败,该方法都会执行,那么就相当于调用了 then
方法,并在传入的回调中执行 finally
的回调
finally(onFinallyed) {
return this.then(
(value) => {
if (isFunc(onFinallyed)) onFinallyed();
return value;
},
(reason) => {
if (isFunc(onFinallyed)) onFinallyed();
throw reason;
}
);
}
finally
传入的回调没有参数,也会忽略返回值,所以直接执行即可
- 由于
finally
方法可以将结果或错误传递给下一个合适的处理程序,相当于调用了 then
方法,并返回传入的参数,让后续的处理方法可以获取
// 如下所示
p2.then((res) => 2)
.finally(() => { console.log('先执行这里的代码') })
.then(console.log); // 2

类方法
Promise
的类方法有 resolve
,reject
,all
,allSettled
,race
,any
,都是需要通过Promise.xxx
调用,所以都需要加上 static
关键字
resolve和reject实现
resolve
和 reject
都是对传入的参数,使用 new Promise()
进行处理
static resolve(value) {
// 判断传入的是否为promise,是则直接返回
if (value instanceof MyPromise) return value;
// 如果传入thenable对象,则直接使用对象中的then方法
if (isObj(value) && isFunc(value.then)) return new MyPromise(value.then);
return new MyPromise((resolve) => resolve(value));
}
static reject(reason) {
return new MyPromise((reslove, reject) => reject(reason));
}
resolve
用于创建一个完成的 promise
,该 promise
的状态取决于传入的参数;reject
无论如何都是返回一个失败状态的 promise

all和allSettled实现
- 这两个方法都是接收一个元素为
promise
的数组,并且返回处理后的结果数组,都是返回一个新 Promise
- 如果传入的数组带有非
promise
元素,则使用 Promise.resolve进行处理
,并且返回结果数组内的顺序,和传入的顺序一致
static all(promises) {
return new MyPromise((resolve, reject) => {
const values = []; // 保存所有结果的数组
let resolvedCount = 0; // 统计promise完成的个数
promises.forEach((promise, index) => {
// 这里统一使用类方法resolve处理
MyPromise.resolve(promise).then(
(value) => {
// 这里使用传入的顺序,存放对应的结果
// 如果使用push则顺序会出现差错
values[index] = value;
// 完成一个累加1
resolvedCount++;
// 使用resolvedCount判断,而不是values.length
// 如果使用values.length判断,会导致结果数组包含空元素
if (resolvedCount === promises.length) resolve(values);
},
// 只要有异常立马抛出
(reason) => reject(reason)
);
});
});
}
static allSettled(promises) {
return new MyPromise((resolve, reject) => {
const results = []; // 保存所有结果的数组
let finishedCount = 0; // 统计完成的个数,无论是失败还是成功都算完成
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
(value) => {
results[index] = { status: PROMISE_STATUS.FULFILLED, value };
finishedCount++;
if (finishedCount === promises.length) resolve(results);
},
(reason) => {
results[index] = { status: PROMISE_STATUS.REJECTED, reason };
finishedCount++;
if (finishedCount === promises.length) resolve(results);
}
);
});
});
}
- 其中
all
和 allSettled
不同的是:all
方法中只要有一个 promise
失败则直接 reject
,否则等待所有 promise
完成后才将结果 resolve
;而 allSettled
方法不管是失败还是成功都会保存到结果数组中,等待都完成后才 resolve
race和any
- 这两个方法都是接收一个元素为
promise
的数组,都是返回一个新 Promise
race
方法只要有一个 promise
完成,则立即调用新 Promise
的resolve
或reject
- 而
any
方法是等待第一个成功的 promise
完成后,才调用新 Promise
的resolve
或reject
,如果所有promise
都失败了则将错误数组进行 reject
,并抛出 AggregateError
错误
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
// 使用类方法resolve进行包装,并且只要有完成的直接resolve或reject
MyPromise.resolve(promise).then(resolve, reject);
});
});
}
static any(promises) {
return new MyPromise((resolve, reject) => {
const reasons = []; // 存放所有的错误结果
let rejectedCount = 0; // 计算错误的次数
promises.forEach((promise, index) => {
// 使用类方法resolve进行包装,并且只要有完成的直接resolve
MyPromise.resolve(promise).then(resolve, (reason) => {
// 如果是错误的promise
reasons[index] = reason; // 先保存到数组中
rejectedCount++; // 然后累加错误次数
// 如果错误次数和传入的promise数组相等,则说明是全都失败了
if (rejectedCount === promises.length) reject(new AggregateError(reasons));
});
});
});
}