myPromise
由浅入深的实现一个属于自己的
Promise
,并且实现Promise
的大部分功能
1、执行器
原版
首先我们由浅入深的了解一下Promise
的使用。
- 我们会传给
Promise
一个函数,函数会被立即执行,这个函数我把它叫做执行器。 - 会返回一个
Promise
对象 包含状态(State
) 和 结果(result
)
模仿
编写 myPromise.js
难度不大 我就解释了
const PENDING = 'pending'; // 等待 状态
const FULFILLED = 'fulfilled'; // 成功 状态
const REJECTED = 'rejected'; // 失败 状态
class myPromise {
constructor(executor) {
executor();
}
// 当前的状态
state = PENDING;
// 结果值
result = undefined;
}
测试
ok,到此我们Promise最简单的一个功能我们已经实现
2、执行器参数和 then 方法
针对上面,我们继续加深一下Promise
的使用。
原版
- 执行器会接收两个参数,
resolve
和reject
- 在执行器中执行了
resolve
或reject
会改变当前对象的状态,resolve
方法会把状态变成成功(fulfilled
),reject
方法会把状态变成失败(rejected
) - 已经改变状态的
Promise
对象的状态不能再改变。就是说多次调用resolve
和reject
只会执行第一次调用的那个方法 promise
对象有一个then
方法,会接收两个参数,第一个参数是成功的回调,第二个参数是失败回调- 在成功回调函数中,接收成功的值,也就是再执行器中调用
resolve
方法传递的值 - 在失败回调函数中,接收失败的值,也就是再执行器中调用
reject
方法传递的值
- 在成功回调函数中,接收成功的值,也就是再执行器中调用
模仿
编写 myPromise.js
- 我们先定义一个
resolve
方法,先判断当前的状态,只有等待状态才会继续,紧接着我们修改当前的状态是成功的并且保存成功值 reject
方法同上- 接着我们需要在执行器中,传入我们自己的
resolve
和reject
,这里需要注意一下,真正调用resolve
和reject
的时候是在使用者调用的环境,比如浏览器,但是resolve
做的事情是要对当前对象进行处理,所以用bind
处理一下this指向 - 接着实现
then
方法,判断当前的状态,执行对应的回调函数
const PENDING = 'pending'; // 等待 状态
const FULFILLED = 'fulfilled'; // 成功 状态
const REJECTED = 'rejected'; // 失败 状态
class myPromise {
constructor(executor) {
executor(this.resolve.bind(this), this.reject.bind(this));
}
// 当前的状态
state = PENDING;
// 结果值
result = undefined;
resolve(val) {
// 状态不是 pending 就直接返回
if (this.state !== PENDING) return;
// 修改状态为成功状态
this.state = FULFILLED;
// 绑定成功结果
this.result = val;
}
reject(val) {
// 状态不是 pending 就直接返回
if (this.state !== PENDING) return;
// 修改状态为失败状态
this.state = REJECTED;
// 绑定失败结果
this.result = val;
}
then(successCb, failCb) {
if (this.state === FULFILLED) {
successCb(this.result);
} else if (this.state === REJECTED) {
failCb(this.result);
}
}
}
测试
这个时候,myPromise
基本就成型了,紧接着还有许许多多的问题需要我们处理,一步一步来吧。
3、执行器中的异步代码
看一下上面的例子可以发现,我在写原版的时候是用了1ms
的定时器模拟了网络请求,但是在myPromise
中并没有写定时器。因为我们现在的myPromse
还不支持在执行器中有异步代码。
想一下,代码是同步执行的,执行器执行的时候,resolve
或reject
是延迟的,那再执行到下面then
方法的时候,当前myPromise
的状态还是等待状态,就不会执行成功或者失败回调了
如何来改造呢?
myPromise
中的then
方法里面,判断一下当前的状态,如果是等待的状态,我们就要想办法把传入的成功/失败回调函数,要放在 resolve
/reject
后执行。
ok,思路有了,改造myPromise
代码。
- 再
then
方法中,如果当前状态是等待状态,就需要把 成功回调或者失败回调保存起来 - 再
myPromise
类中定义两个变量用来保存then
方法的成功回调和失败回调 - 再
resolve/reject
方法中,最后判断一下有没有成功/失败回调,有的话就需要调用一下
const PENDING = 'pending'; // 等待 状态
const FULFILLED = 'fulfilled'; // 成功 状态
const REJECTED = 'rejected'; // 失败 状态
class myPromise {
constructor(executor) {
executor(this.resolve.bind(this), this.reject.bind(this));
}
// 当前的状态
state = PENDING;
// 结果值
result = undefined;
// 成功回调
successCallback = undefined;
// 失败回调
failCallback = undefined;
resolve(val) {
// 状态不是 pending 就直接返回
if (this.state !== PENDING) return;
// 修改状态为成功状态
this.state = FULFILLED;
// 绑定成功结果
this.result = val;
// 判断成功回调是否存在 如果存在 调用
this.successCallback && this.successCallback(this.result);
}
reject(val) {
// 状态不是 pending 就直接返回
if (this.state !== PENDING) return;
// 修改状态为失败状态
this.state = REJECTED;
// 绑定失败结果
this.result = val;
// 判断失败回调是否存在 如果存在 调用
this.failCallback && this.failCallback(this.result);
}
then(successCb, failCb) {
if (this.state === FULFILLED) {
successCb(this.result);
} else if (this.state === REJECTED) {
failCb(this.result);
} else {
// 当前状态是 等待状态,那么成功或者失败的回调要再 resolve/reject之后执行
this.successCallback = successCb;
this.failCallback = failCb;
}
}
}
4、多次 then
promise
的then
方法是可以被多次调用的。
原版
模仿
目前,如果执行器中的代码是同步的,myPromise
也一样是可以实现多次then
的,如果执行器中存在了异步的代码,因为我们再then
方法中对等待状态的回调函数做的是一个赋值操作,那么多次的then
的话,目前的情况下我们只会执行最后一个then
方法,因为前面的都被覆盖了。
如何来改造呢?
我们现在需要改造存储then
方法的回调函数的地方,之前是直接赋值,现在需要实现多次then
能保存下来每一次then
的回调函数,在 resolve
/reject
执行的时候也需要修改,之前是执行回调函数,现在需要按顺序执行回调函数。
编写 myPromise.js
- 修改存储成功/失败回调函数的地方,让他默认值是一个数组,这样就可以缓存多个回调函数了
- 在
then
方法接触到等待状态的时候,把成功/失败回调函数存到数组里面 - 在
resolve/reject
的时候,在回调函数数组中,从头取出每一个回调函数并且执行
const PENDING = 'pending'; // 等待 状态
const FULFILLED = 'fulfilled'; // 成功 状态
const REJECTED = 'rejected'; // 失败 状态
class myPromise {
constructor(executor) {
executor(this.resolve.bind(this), this.reject.bind(this));
}
// 当前的状态
state = PENDING;
// 结果值
result = undefined;
// 成功回调
successCallbackList = [];
// 失败回调
failCallbackList = [];
resolve(val) {
// 状态不是 pending 就直接返回
if (this.state !== PENDING) return;
// 修改状态为成功状态
this.state = FULFILLED;
// 绑定成功结果
this.result = val;
// 多次的then需要按顺序执行成功的回调函数
while (this.successCallbackList.length) this.successCallbackList.shift()(this.result);
}
reject(val) {
// 状态不是 pending 就直接返回
if (this.state !== PENDING) return;
// 修改状态为失败状态
this.state = REJECTED;
// 绑定失败结果
this.result = val;
// 多次的then需要按顺序执行失败的回调函数
while (this.failCallbackList.length) this.failCallbackList.shift()(this.result);
}
then(successCb, failCb) {
if (this.state === FULFILLED) {
successCb(this.result);
} else if (this.state === REJECTED) {
failCb(this.result);
} else {
// 当前状态是 等待状态,那么成功或者失败的回调要再 resolve/reject之后执行
this.successCallbackList.push(successCb);
this.failCallbackList.push(failCb);
}
}
}
测试
5、微任务
原版
promise
的then
方法中的回调函数是被加入到了微任务队列中的,执行的顺序会晚于主程序。
模仿
我们现在执行器代码中因为有同步和异步的区别,在then中判断了当前myPromise
的状态,进行了分别的处理。在同步的情况下,代码会立即执行对应的回调函数。
异步的情况下,代码是丢到了回调函数队列中,在resolve或reject
中执行的。
所以我们其实这里都没有用到微任务,都需要做一个微任务。
那么,我们现在可以在then
中不判断状态,直接把回调函数丢到回调函数队列中,在resolve或reject
中遍历回调函数队列并且执行的这个过程我们可以丢到微任务队列中。
编写 myPromise.js
const PENDING = 'pending'; // 等待 状态
const FULFILLED = 'fulfilled'; // 成功 状态
const REJECTED = 'rejected'; // 失败 状态
class myPromise {
constructor(executor) {
executor(this.resolve.bind(this), this.reject.bind(this));
}
// 当前的状态
state = PENDING;
// 结果值
result = undefined;
// 成功回调
successCallbackList = [];
// 失败回调
failCallbackList = [];
resolve(val) {
// 状态不是 pending 就直接返回
if (this.state !== PENDING) return;
// 修改状态为成功状态
this.state = FULFILLED;
// 绑定成功结果
this.result = val;
this._nextTick(() => {
// 多次的then需要按顺序执行成功的回调函数
while (this.successCallbackList.length) this.successCallbackList.shift()(this.result);
});
}
reject(val) {
// 状态不是 pending 就直接返回
if (this.state !== PENDING) return;
// 修改状态为失败状态
this.state = REJECTED;
// 绑定失败结果
this.result = val;
this._nextTick(() => {
// 多次的then需要按顺序执行失败的回调函数
while (this.failCallbackList.length) this.failCallbackList.shift()(this.result);
});
}
then(successCb, failCb) {
this.successCallbackList.push(successCb);
this.failCallbackList.push(failCb);
}
// Promise -> MutationObserver -> setImmediate -> setTimeout
_nextTick(cb) {
// 执行回调函数
const p = Promise.resolve();
p.then(cb);
// -------
// MutationObserver 实现微任务
// let ob = new MutationObserver(() => {
// cb();
// // 用完销毁
// ob.disconnect();
// ob = null;
// });
// ob.observe(document.body, { attributes: true });
// document.body.setAttribute('_yl', Math.random());
// -------
// setTimeout 实现宏任务
// setTimeout(cb, 0);
}
}
resolve或reject
执行的时候把执行回调函数队列的过程丢到了_nextTick
方法中_nextTick
的实现借鉴了vue
,主要就是判断当前环境下,Promise -> MutationObserver -> setImmediate -> setTimeout
这些变量存不存在,优先级按照箭头顺序,哪个存在用哪个对象实现异步。then
方法现在不管三七二十一直接把回调函数丢到队列里面了,为什么现在可以这样做,之前的时候要去判断myPromise
的状态然后还要根据不同的状态做不同的事情?- 因为遍历回调函数队列的过程是丢到了异步任务中,我们之前在写代码
.then
进行注册事件的这个过程是同步过程,现在then
的回调函数一定会先被注册,这个异步任务就能保证在遍历回调函数队列的时候,then
的方法一定已经被注册了。 - 或者说,之前不能这样写的原因是,执行 同步
resolve
的时候,then
还没有被执行,那么resolve
方法遍历回调函数队列的时候就遍历了空数组了。现在遍历的过程变成了异步了,then方法会先执行,遍历回调函数队列后执行,回调函数队列就一定是有值的。
- 因为遍历回调函数队列的过程是丢到了异步任务中,我们之前在写代码
6、链式 then
原版
promise
的then
方法是支持链式调用的- 后面
then
的回调函数中的参数是上一次then
中成功回调的返回值。- 情况一:如果
then
中成功回调返回的是一个普通值,下一个then
接收的就是这个普通值 - 情况二:如果
then
中成功回调返回的是一个myPromise
对象,下一个then
接收的就是这个myPromise
对象resolve
的结果
- 情况一:如果
- 如果前面的
promise
变成失败状态了,在then
的失败回调函数中会被捕获到,那么下一个then
的参数会使用失败回调的返回值
模仿
首先我们要明确,then
链式调用的原理是 then
方法需要返回一个myPriomise
对象,为了好记,我们给返回的对象叫做thenPromise
,改造一下then
方法
then(successCb, failCb) {
const thenPromise = new myPromise((thenResolve, thenReject) => {
this.successCallbackList.push(successCb);
this.failCallbackList.push(failCb);
});
return thenPromise;
}
然后我们需要清楚,链式调用中,后面的then
拿到的参数是哪来的,不难理解,后面的then
的参数需要我们这个thenPromise
执行thenResolve
方法。
这里我们还需要清楚一点,successCb
这个函数是我们自定义的没问题,那这个函数干了什么,返回了什么都是自定义的,但是这个函数的参数是myPromise
提供的,是在myPromise
中执行resolve
方法的时候传入的当前myPromise
的result
。
返回普通对象
- 针对情况一,前面
then
方法的成功回调返回的是一个普通值,那么我们只需要拿到successCb
的返回值并且thenResolve
出来就可以。代码如下
then(successCb, failCb) {
const thenPromise = new myPromise((thenResolve, thenReject) => {
this.successCallbackList.push((thenResult) => {
// 执行成功回调函数,拿到返回值
thenResult = successCb(thenResult);
// resolve出这个值,链式调用的时候后面的then就可以拿到了
thenResolve(thenResult);
});
this.failCallbackList.push((thenResult) => {
// 执行失败回调函数,拿到返回值
thenResult = failCb(thenResult);
// 失败函数的返回值是被下一个then成功回调接收的 这里需要执行thenResolve
thenResolve(thenResult);
});
});
return thenPromise;
}
返回myPromise
对象
- 针对情况二,前面
then
方法的成功回调返回的是一个myPromise
对象,也就是说我们执行successCb
拿到的返回值thenResult
是一个myPromise
对象,那么链式调用的时候,后面的then
拿到的值应该是thenResult
这个myPromise
对象的result
结果,也就是说我们返回的thenPromise
需要拿到thenResult
这个myPromise
对象的result
。- 这里逻辑很绕,希望能认真的分析一下
thenResult
是一个myPromise
对象- 我们返回的
thenPromise
需要拿到thenResult
成功状态的result
- 拿到的意思也就是说
thenResolve
方法要获取到thenResult
成功状态的result
- 获取
thenResult
成功状态的result
可以让thenResult
调用一下then
方法,这个then
方法可以接收到result
,这个时候我们在直接thenResolve
一下就可以了
ok 逻辑清楚了就上代码
then(successCb, failCb) {
const thenPromise = new myPromise((thenResolve, thenReject) => {
this.successCallbackList.push((thenResult) => {
// 执行成功回调函数,拿到返回值
thenResult = successCb(thenResult);
// 如果thenResult 是一个myPromise对象
if (thenResult instanceof myPromise) {
// thenResolve 需要拿到thenResult这个myPromise对象的result
return thenResult.then(
(thenResultPromiseResult) => thenResolve(thenResultPromiseResult),
(thenResultPromiseResult) => thenReject(thenResultPromiseResult)
);
}
// resolve出这个值,链式调用的时候后面的then就可以拿到了
thenResolve(thenResult);
});
this.failCallbackList.push((thenResult) => {
// 执行失败回调函数,拿到返回值
thenResult = failCb(thenResult);
// 如果thenResult 是一个myPromise对象
if (thenResult instanceof myPromise) {
// thenResolve 需要拿到thenResult这个myPromise对象的result
return thenResult.then(
(thenResultPromiseResult) => thenResolve(thenResultPromiseResult),
(thenResultPromiseResult) => thenReject(thenResultPromiseResult)
);
}
// 失败函数的返回值是被下一个then成功回调接收的 这里需要执行thenResolve
thenResolve(thenResult);
});
});
return thenPromise;
}
看到这里,我希望你现在没有啥疑问了在接着往下看,因为这里确实很绕。
写道这里,代码其实可以优化一下
function test(val){ return Math.ceil(val); }
// 如果一个方法 直接返回了一个方法的调用,参数一致的情况下
test === Math.ceil // 等式成立
// 那么
(thenResultPromiseResult) => thenResolve(thenResultPromiseResult) === thenResolve // 等式成立
所以我们的代码可以简化一下
then(successCb, failCb) {
const thenPromise = new myPromise((thenResolve, thenReject) => {
this.successCallbackList.push((thenResult) => {
// 执行成功回调函数,拿到返回值
thenResult = successCb(thenResult);
// 如果thenResult 是一个myPromise对象
if (thenResult instanceof myPromise) {
// thenResolve 需要拿到thenResult这个myPromise对象的result
return thenResult.then(thenResolve, thenReject);
}
// resolve出这个值,链式调用的时候后面的then就可以拿到了
thenResolve(thenResult);
});
this.failCallbackList.push((thenResult) => {
// 执行失败回调函数,拿到返回值
thenResult = failCb(thenResult);
// 如果thenResult 是一个myPromise对象
if (thenResult instanceof myPromise) {
// thenResolve 需要拿到thenResult这个myPromise对象的result
return thenResult.then(thenResolve, thenReject);
}
// 失败函数的返回值是被下一个then成功回调接收的 这里需要执行thenResolve
thenResolve(thenResult);
});
});
return thenPromise;
}
这里做一个小解释,在情况二的时候我们写了
return
进行返回了一个值,其实这个值是无意义的,我们链式调用关注的是then
方法的返回值,我们并不关注传给then
的successCb
方法的返回值,写return
的主要目的是防止代码继续向下执行而报错
测试
- 测试返回普通值
- 测试返回
myPromise
7、异常捕获
发生异常的地方应该是两个,一个是执行器执行的时候可能会异常,一个是传给then
的两个函数执行可能异常。
如果捕获到了异常,我们直接调用reject
方法就可以了
constructor
修改
constructor(executor) {
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (e) {
this.reject(e);
}
}
then
修改
then(successCb, failCb) {
const thenPromise = new myPromise((thenResolve, thenReject) => {
this.successCallbackList.push((thenResult) => {
try {
// 执行成功回调函数,拿到返回值
thenResult = successCb(thenResult);
// 如果thenResult 是一个myPromise对象
if (thenResult instanceof myPromise) {
// thenResolve 需要拿到thenResult这个myPromise对象的result
return thenResult.then(thenResolve, thenReject);
}
// resolve出这个值,链式调用的时候后面的then就可以拿到了
thenResolve(thenResult);
} catch (e) {
thenReject(e);
}
});
this.failCallbackList.push((thenResult) => {
try {
// 执行失败回调函数,拿到返回值
thenResult = failCb(thenResult);
// 如果thenResult 是一个myPromise对象
if (thenResult instanceof myPromise) {
// thenResolve 需要拿到thenResult这个myPromise对象的result
return thenResult.then(thenResolve, thenReject);
}
// 失败函数的返回值是被下一个then成功回调接收的 这里需要执行thenResolve
thenResolve(thenResult);
} catch (e) {
thenReject(e);
}
});
});
return thenPromise;
}
测试
- 测试执行器异常
- 测试链式调用then异常
7.5、小小的撒花
至此
myPromise
的核心部分已经实现的差不多了,小小的撒花一下
8、then
参数优化
原版
promise
的then
如果不传参数的话是不会执行的,会将状态传递下去
模仿
链式调用已经实现了,现在要做没有参数的时候的 状态传递。
上面的代码我们换一种思路理解一下
.then().then()
// 等价交换
.then(val => val).then(val => val)
这样写是不是思路就很清楚了,那么我们接下来改造一下then方法
代码如下
then(successCb, failCb) {
// 参数处理
successCb = successCb ? successCb : (result) => result;
failCb = failCb ? failCb : (result) => {throw result};
......
}
需要注意的是失败的情况下,我们需要把失败状态的myPromise
的result
用异常给抛出来
测试
9、静态all方法实现
原版
all
如何使用我就不演示了,不会的可以看下面的测试用例
模仿
all
方法有几个要注意的点
- 传入的数组中的所有的
myPromise
对象都resolve
了all
方法才可以resolve
all
方法then
接收到的结果的顺序 需要和 传递数组中的顺序保持一致
static all(array) {
return new myPromise((resolve, reject) => {
const arrayLen = array.length; // 数组的长度
const res = []; //返回结果
let fulfilledNum = 0; //成功的个数
function addData(index, value) {
// 顺序一致需要采用索引赋值而不是push
res[index] = value;
// 记录成功的个数 只有全部成功了 all 才 resolve
fulfilledNum += 1;
if (fulfilledNum === arrayLen) {
resolve(res);
}
}
array.forEach((item, index) => {
if (item instanceof myPromise) {
// myPromise 对象
item.then(
(val) => addData(index, val),
(err) => reject(err)
);
} else {
// 其他情况
addData(index, item);
}
});
});
}
测试
10、静态resolve方法实现
模仿
- 传入普通值就返回一个成功状态 并且结果是 传入值的
myPromise
- 传入的是
myPromise
对象,则直接返回传入值
static resolve(value) {
if (value instanceof myPromise) return value;
return new myPromise((resolve) => resolve(value));
}
测试
11、静态reject方法实现
模仿
参考上面的 我就不贴写测试过程了
static reject(err) {
if (err instanceof myPromise) return err;
return new myPromise((resolve, reject) => reject(err));
}
12、finally实例方法实现
- 链式调用的时候,
finally
会返回一个promise
,这个promise
并不是一个新的promise
,而是返回了自己 finally
传入一个函数,不管自己的状态是什么都会执行finally
的回调函数的返回值并不会影响到后面的then
,但是如果返回了一个promise对象的话,会等待整个promise对象状态改变之后再继续执行后面的then
,而且只是等待返回的promise
的状态变化,并不会影响后面的then
第一版
finally(cb) {
return this.then(
// value 是 自己的resolve执行的时候传过来的自己的结果
(value) => {cb(); return value},
(err) => {cb(); return {throw err}},
);
}
这一版考虑到了情况一和二,但是没有考虑到 cb
函数的返回值是一个 promise
对象时候的情况
第二版
finally(cb) {
return this.then(
// 包了一层myPromise.resolve 是为了等待cb函数返回的promise状态改变之后再返回 自己的value
(value) => myPromise.resolve(cb()).then(() => value),
(err) => myPromise.resolve(cb()).then(() => {throw err })
);
}
13、实现catch
catch的实现等价于一个有失败回调函数没有成功回调函数的then方法
catch(failCb) {
return this.then(undefined, failCb);
}
完结撒花-最终代码
const PENDING = 'pending'; // 等待 状态
const FULFILLED = 'fulfilled'; // 成功 状态
const REJECTED = 'rejected'; // 失败 状态
class myPromise {
constructor(executor) {
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (e) {
this.reject(e);
}
}
// 当前的状态
state = PENDING;
// 结果值
result = undefined;
// 成功回调
successCallbackList = [];
// 失败回调
failCallbackList = [];
resolve(val) {
// 状态不是 pending 就直接返回
if (this.state !== PENDING) return;
// 修改状态为成功状态
this.state = FULFILLED;
// 绑定成功结果
this.result = val;
this._nextTick(() => {
// 多次的then需要按顺序执行成功的回调函数
while (this.successCallbackList.length) this.successCallbackList.shift()(this.result);
});
}
reject(val) {
// 状态不是 pending 就直接返回
if (this.state !== PENDING) return;
// 修改状态为失败状态
this.state = REJECTED;
// 绑定失败结果
this.result = val;
this._nextTick(() => {
// 多次的then需要按顺序执行失败的回调函数
while (this.failCallbackList.length) this.failCallbackList.shift()(this.result);
});
}
then(successCb, failCb) {
// 参数处理
successCb = successCb ? successCb : (value) => value;
failCb = failCb
? failCb
: (errStr) => {
throw errStr;
};
const thenPromise = new myPromise((thenResolve, thenReject) => {
this.successCallbackList.push((thenResult) => {
try {
// 执行成功回调函数,拿到返回值
thenResult = successCb(thenResult);
// 如果thenResult 是一个myPromise对象
if (thenResult instanceof myPromise) {
// thenResolve 需要拿到thenResult这个myPromise对象的result
return thenResult.then(thenResolve, thenReject);
}
// resolve出这个值,链式调用的时候后面的then就可以拿到了
thenResolve(thenResult);
} catch (e) {
thenReject(e);
}
});
this.failCallbackList.push((thenResult) => {
try {
// 执行失败回调函数,拿到返回值
thenResult = failCb(thenResult);
// 如果thenResult 是一个myPromise对象
if (thenResult instanceof myPromise) {
// thenResolve 需要拿到thenResult这个myPromise对象的result
return thenResult.then(thenResolve, thenReject);
}
// 失败函数的返回值是被下一个then成功回调接收的 这里需要执行thenResolve
thenResolve(thenResult);
} catch (e) {
thenReject(e);
}
});
});
return thenPromise;
}
static all(array) {
return new myPromise((resolve, reject) => {
const arrayLen = array.length; // 数组的长度
const res = []; //返回结果
let fulfilledNum = 0; //成功的个数
function addData(index, value) {
// 顺序一致需要采用索引赋值而不是push
res[index] = value;
// 记录成功的个数 只有全部成功了 all 才 resolve
fulfilledNum += 1;
if (fulfilledNum === arrayLen) {
resolve(res);
}
}
array.forEach((item, index) => {
if (item instanceof myPromise) {
// myPromise 对象
item.then(
(val) => addData(index, val),
(err) => reject(err)
);
} else {
// 其他情况
addData(index, item);
}
});
});
}
finally(cb) {
return this.then(
// value 是 自己的resolve执行的时候传过来的自己的结果
(value) => myPromise.resolve(cb()).then(() => value),
// 包了一层myPromise.resolve 是为了等待cb函数返回的promise状态改变之后再返回 自己的value
(err) =>
myPromise.resolve(cb()).then(() => {
throw err;
})
);
}
catch(failCb) {
return this.then(undefined, failCb);
}
static resolve(value) {
if (value instanceof myPromise) return value;
return new myPromise((resolve) => resolve(value));
}
static reject(err) {
if (err instanceof myPromise) return err;
return new myPromise((resolve, reject) => reject(err));
}
// Promise -> MutationObserver -> setImmediate -> setTimeout
_nextTick(cb) {
// 执行回调函数
const p = Promise.resolve();
p.then(cb);
// -------
// MutationObserver 实现微任务
// let ob = new MutationObserver(() => {
// cb();
// // 用完销毁
// ob.disconnect();
// ob = null;
// });
// ob.observe(document.body, { attributes: true });
// document.body.setAttribute('_yl', Math.random());
// -------
// setTimeout 实现宏任务
// setTimeout(cb, 0);
}
}