手写Promise


一步一步教你手写Promise

写这篇文章主要目的是为了巩固自己的知识点以及更深入了解promise的原理,对于初学者有一些不友好,建议初学者先去了解promise基本用法再来看这篇文章。从零开始一步一步教学写then、all、catch、race、Promise.resolve、Promise.reject,原理的话一边写一边给大家介绍,那我们就开始吧!

第一步:初始结构搭建

我们都知道Promise总共有三种状态,分别是pending、fulfilled、reject,三种状态不可逆,通过调用resolve、reject以及抛出错误可以改变状态,只能是pending–>fulfilled或者是pending–>reject,我们throw利用try catch去捕捉错误信息

function Promise(executor){
    //添加属性,一开始的状态为pending
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    //保存实例对象的this值
    const self = this;
    //resolve 函数
    function resolve(data){
        // 判断状态(解决状态可逆)若状态不为pending下面代码则无法执行直接return
        if(self.PromiseState !== 'pending') return
        //修改对象状态(promiseState)
        self.PromiseState = 'fulfilled';
        //设置对象结果值(promiseResult)
        self.PromiseResult = data;
    }
    //reject 函数
    function reject(data){
        // 判断状态(解决状态可逆)若状态不为pending下面代码则无法执行直接return
        if(self.PromiseState !== 'pending') return
        //修改对象状态(promiseState)
        self.PromiseState = 'rejectd';
        //设置对象结果值(promiseResult)
        self.PromiseResult = data;
    }
    try{
    //同步调用执行器函数,执行resolve和reject
    executor(resolve,reject);
    }catch(e){
        //若抛出错误的话则会变成reject状态
        reject(e);
    }
}
//给原型添加then方法,这样实例对象可以用then方法
Promise.prototype.then = function(onResolved,onRejected){
    
}
  • 这样一开始的结构差不多就搭建完啦,自己可以去验证一下

TjEl5V.png

TjEwUx.md.png

第二步:then方法

1.then方法执行回调

Promise.then要传两个回调函数,一个是当状态为fulfilled的回调函数另一个则为状态为reject状态的回调函数

Promise.prototype.then = function (onResolved, onRejected) {
    if (this.PromiseState === 'fulfilled') {
        onResolved(this.PromiseResult);
    }
    if (this.PromiseState === 'rejected') {
        onRejected(this.PromiseResult);
    }
}

得到的结果如下图所示:

TjcuLT.md.png

TjcwwD.md.png

当然这还不是最终版我们还会进行完善,请继续往下看

2.异步任务回调的执行

现在假如有一个场景需要有异步任务回调执行,过几秒才会变状态,这怎么执行呢,所以我们要继续完善我们的代码,使其可以异步任务回调的执行,若在我们刚刚写的代码加入异步任务回调会怎么样呢?

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('123');
    }, 1000)
})
p.then(value => {
    console.log(value);
}, reason => {
    console.warn(reason);
})

答案是:没有输出,没有执行。经过一秒才会去改变状态,所以进入then回调函数的时候他的一开始状态是处于pending状态而我们的代码没有去判断这个状态的函数

1.所以我们要去完善并判断当一开始状态是pending的情况。

2.当我们状态还是pending状态时候,我们怎么实现当状态发生改变的时候去执行回调函数呢,状态未发生变化则不能执行回调函数,等状态改变再去调用保存的回调函数, 这时候就用的有点巧妙了,先将回调函数进行保存,我们新建一个callback属性用来保存回调函数(+++号表示新增代码)

3.4.回调函数保存后,我们该什么时候去调用它呢,我们需要状态改变后去调用onResolvedonRejected函数,则我们找到resolve,reject进行编写,我们去判断callback里面是否有回调函数,如果有的话则在状态之后调用函数

function Promise(executor) {
    //添加属性,一开始的状态为pending
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // 声明属性保存回调函数
    this.callback = {};  //2.++++++++++++++++
    //保存实例对象的this值
    const self = this;
    //resolve 函数
    function resolve(data) {
        // 判断状态(解决状态可逆)
        if (self.PromiseState !== 'pending') return
        //修改对象状态(promiseState)
        self.PromiseState = 'fulfilled';
        //设置对象结果值(promiseResult)
        self.PromiseResult = data;
        //调用成功的回调函数   3.++++++++++++
        if(self.callback.onResolved){
            self.callback.onResolved();
        }
    }
    //reject 函数
    function reject(data) {
        // 判断状态(解决状态可逆)
        if (self.PromiseState !== 'pending') return
        //修改对象状态(promiseState)
        self.PromiseState = 'rejected';
        //设置对象结果值(promiseResult)
        self.PromiseResult = data;
        //调用失败的回调函数       4.++++++++
        if(self.callback.onRejected){
            self.callback.onRejected();
        }
    }
    try {
        //同步调用执行器函数,执行resolve和reject
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

当然我们的then方法也得进行更新

Promise.prototype.then = function (onResolved, onRejected) {
    //成功状态
    if (this.PromiseState === 'fulfilled') {
        onResolved(this.PromiseResult);
    }
    //失败状态
    if (this.PromiseState === 'rejected') {
        onRejected(this.PromiseResult);
    }
    //异步状态处理
    if(this.PromiseState === 'pending'){  //1.++++++++++
        //保存回调函数 
		this.callback = {    
            onResolved,  //键值和键名相等简写
            onRejected
        }
    }   //+++++++
}

现在打印p进行查看则有callback属性(回调函数)

Tj48H0.md.png

现在我们的then方法就可以异步任务回调的执行啦,将上面setTimeout内行代码执行在控制台进行查看,过一秒输出结果,如下图所示

TjT2dI.md.png

==注意!!==

我们等一秒输出结果之后再点开promise对象状态会变成fulfilled,若还未输出结果就点开promise对象则状态还是pending

3.指定多个回调函数

当我们执行下列代码会发生什么呢

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('123');
    }, 1000);
})
p.then(value => {
    console.log(value);
}, reason => {
    console.warn(reason);
})
p.then(value => {
    alert(value);
}, reason => {
    alert(reason);
})
console.log(p)

用内置的Promise则会先输出123然后再弹出一个alert 123的弹窗,而用我们刚刚自己写的Promise会发生什么呢?答案是只会弹出一个alert 123弹窗,为什么会这样子呢?因为后面的回调函数覆盖了前面console.log的回调所以只有一个弹窗输出,所以我们还要进行代码改进

1。对于回调函数的保存我们要进行改变,声明一个callbacks数组保存回调函数,上一步我们用的是对象进行保存。

2.然后将每个回调函数作为对象push到callbacks这个数组里面

3.此时数组存放多个对象,每个对象则有onResolved,onRejected回调函数,利用forEach遍历数组执行回调函数

function Promise(executor) {
    //添加属性,一开始的状态为pending
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // 声明属性(改用数组的方法) 1.+++++++++
    this.callbacks= [];
    //保存实例对象的this值
    const self = this;
    //resolve 函数
    function resolve(data) {
        // 判断状态(解决状态可逆)
        if (self.PromiseState !== 'pending') return
        //修改对象状态(promiseState)
        self.PromiseState = 'fulfilled';
        //设置对象结果值(promiseResult)
        self.PromiseResult = data;
        //调用成功的回调函数  3.+++++++
        self.callbacks.forEach(item => {
            item.onResolved(data)
        })
    }
    //reject 函数
    function reject(data) {
        // 判断状态(解决状态可逆)
        if (self.PromiseState !== 'pending') return
        //修改对象状态(promiseState)
        self.PromiseState = 'rejected';
        //设置对象结果值(promiseResult)
        self.PromiseResult = data;
        //调用失败的回调函数
        self.callbacks.forEach(item => { //4.++++++++
            item.onRejected(data)
        })
    }
    try {
        //同步调用执行器函数,执行resolve和reject
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}
//给原型添加then方法,这样实例对象可以用then方法
Promise.prototype.then = function (onResolved, onRejected) {
    if (this.PromiseState === 'fulfilled') {
        onResolved(this.PromiseResult);
    }
    if (this.PromiseState === 'rejected') {
        onRejected(this.PromiseResult);
    }
    if(this.PromiseState === 'pending'){
        this.callbacks.push({ // 2.+++++++
            onResolved,
            onRejected
        })
    }
}
  • 这样我们就又完成了一个小点,将上述代码运行可得下列结果TjbXFI.md.png

4.同步修改状态then方法结果返回

我们都知道调用then后返回的对象是一个promise对象,我们用内置的Promise来执行一下下面的结果

let p = new Promise((resolve, reject) => {
    resolve('ok');
})
const result = p.then(value => {
    console.log(value);
},reason => {
    console.log(reason)
})
console.log(result)

结果如下图所示:

7SNMiq.png

用我们上面自己写的Promise则不会返回一个Promise对象,我们继续再来改写一下

1.我们在then方法里面套一个Promise,确保then返回的是一个Promise对象

2.首先我们要明白如果在then里面没有一个返回值也就是没有return,它返回的是一个成功的Promise对象,若then里面又return一个Promise对象则是看里面的Promise是什么状态返回的对象就是什么状态,当我们处于成功状态的时候会执行onResolved回调,失败则会调用onRejected回调

3.首先判断then里面onResolved回调返回的是一个Promise对象的话,则我们可以运行.then方法获取结果,若返回的不是一个Promise的话,则直接去调用resolve方法使状态变为成功

4.若我们在then回调里抛出一个错误,则返回的对象也是一个失败的状态,跟上面捕捉错误方法一样去利用try catch

Promise.prototype.then = function (onResolved, onRejected) {
    return new Promise((resolve, reject) => {    //1.++++++++
        if (this.PromiseState === 'fulfilled') {
            try {    //4.++++++++
                // 获取回调函数的执行结果
                let result = onResolved(this.PromiseResult);  
                //判断
                if (result instanceof Promise) {     //2.+++++++
                    //如果是Promise类型的对象
                    result.then(v => {
                        resolve(v);
                    }, r => {
                        reject(r);
                    })
                } else {   // 3.+++++++
                    //结果的对象状态为成功
                    resolve(result);
                }
            } catch (e) {
                //若检测到有抛出错误时则变为失败的状态
                reject(e);
            }
        }
        if (this.PromiseState === 'rejected') {
            onRejected(this.PromiseResult);
        }
        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved,
                onRejected
            })
        }

    })
}

==我们用我们写的Promise运行下面代码==

//当then里面回调返回是一个Promise
let p = new Promise((resolve, reject) => {
    resolve('ok');
})
const res = p.then(value => {
    return new Promise((resolve,reject) => {
        resolve('123333');
    })
},reason => {
    console.log(reason);
})
console.log(res);
//当then里面回调返回不是一个Promise(无return)
let p1 = new Promise((resolve, reject) => {
    resolve('ok');
})
const res1 = p1.then(value => {
    console.log(value)
},reason => {
    console.log(reason);
})
console.log(res1);
//当then里面抛出一个错误
let p2 = new Promise((resolve, reject) => {
    resolve('ok');
})
const res2 = p2.then(value => {
    throw 'error';
},reason => {
    console.log(reason);
})
console.log(res2);

运行结果如下图所示:

7S5AqH.png

5.异步修改状态then方法结果返回

有人问这步骤刚刚2.2不是已经做过了嘛?答案是:不一样

执行下列代码就知道了

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('ok');                
    }, 1000);

})
const res = p.then(value => {
    console.log(value)
},reason => {
    console.log(reason);
})
console.log(res);

这时候’ok’会经过一秒输出,但是res状态还是处于pending状态并没有发生改变,这是为什么呢?我们在.then方法里面执行回调函数,而我们要根据回调函数去返回res的一个状态,这个意思就是若我们执行onResolved回调的时候我们状态就是成功的并且要返回给Promise对象也是成功的(res)。

1.我们之前在this.PromiseState === ‘pending’时候去直接调用回调这是不可行的,我们稍微做改进变为一个函数,并且把PromiseResult参数传入

2.接下来几步都是和上面一样进行判断啦,判断返回的是否是一个Promise对象,这里就不再重复诉说了

Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    return new Promise((resolve, reject) => {
        if (this.PromiseState === 'fulfilled') {
            try {
                // 获取回调函数的执行结果
                let result = onResolved(this.PromiseResult);
                //判断
                if (result instanceof Promise) {
                    //如果是Promise类型的对象
                    result.then(v => {
                        resolve(v);
                    }, r => {
                        reject(r);
                    })
                } else {
                    //结果的对象状态为成功
                    resolve(result);
                }
            } catch (e) {
                reject(e);
            }
        }
        if (this.PromiseState === 'rejected') {
            onRejected(this.PromiseResult);
        }
        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved: function () {    //1.++++++++
                    try {    //2.下面和上面内个步骤一样具体可以去看2.4+++++++++++
                        //执行成功回调函数
                        let result = onResolved(self.PromiseResult);
                        // 判断
                        if (result instanceof Promise) {
                            result.then(v => {
                                resolve(v);
                            }, r => {
                                reject(r);
                            })
                        } else {
                            resolve(result);
                        }
                    } catch (e) {
                        reject(e);
                    }
                },
                onRejected: function () {  //1.++++++++
                    try {   //2.++++++++
                        onRejected(self.PromiseResult);
                        let result = onRejected(self.PromiseResult);
                        // 判断
                        if (result instanceof Promise) {
                            result.then(v => {
                                resolve(v);
                            }, r => {
                                reject(r);
                            })
                        } else {
                            resolve(result);
                        }
                    } catch (e) {
                        reject(e);
                    }
                }
            })
        }

    })
}

然后我们在运行上面的示范例子的代码可以得到正确的结果,如下图所示:

7SoAUA.png

6.then方法完善和优化

795LSx.png

由我们上述图片可知,我们还未对状态为失败的时候进行改进,所以我们也对失败状态进行更改,详细步骤请见2.4,若不改进的话若Promise里面调用reject(),则状态还会处于pending状态而不是fulfilled状态

if (this.PromiseState === 'rejected') {
try {
    let result = onRejected(this.PromiseResult);
    if(result instanceof Promise){
        result.then(v => {
            resolve(v);
        },r => {
            reject(r);
        })
    }else{
        resolve(result);
    }
} catch (e) {
    reject(e);
}
}

细心的小伙伴有没有发现上面这行代码已经复用很多次啦?我们可以封装一个函数去使代码更加的简洁以及维护的更加方便

//封装函数
function callback(type) {
    try {
        // 获取回调函数的执行结果
        let result = type(this.PromiseResult);
        //判断
        if (result instanceof Promise) {
            //如果是Promise类型的对象
            result.then(v => {
                resolve(v);
            }, r => {
                reject(r);
            })
        } else {
            //结果的对象状态为成功
            resolve(result);
        }
    } catch (e) {
        reject(e);
    }
}

接下里贴一下我们then方法已经变得非常简洁明了啦

Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    return new Promise((resolve, reject) => {
        //封装函数
        function callback(type) {
            try {
                // 获取回调函数的执行结果
                let result = type(self.PromiseResult);   //注意这里面的this指向哦!!记得要更改
                //判断
                if (result instanceof Promise) {
                    //如果是Promise类型的对象
                    result.then(v => {
                        resolve(v);
                    }, r => {
                        reject(r);
                    })
                } else {
                    //结果的对象状态为成功
                    resolve(result);
                }
            } catch (e) {
                reject(e);
            }
        }
        if (this.PromiseState === 'fulfilled') {
            callback(onResolved);
        }
        if (this.PromiseState === 'rejected') {
            callback(onRejected);
        }
        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved: function () {
                    callback(onResolved);
                },
                onRejected: function () {
                    callback(onRejected);
                }
            })
        }

    })
}

!!这样我们的then方法就已经完成啦!!有没有坚持到这里的小伙伴嘛在评论区call个1

第三步:catch方法

接下来我们来封装catch方法,catch方法用来指定失败的回调函数,并且返回结果也是一个Promise对象,这时候我们就可以用到我们已经封装好的then方法

//添加catch方法
Promise.prototype.catch = function(onRejected){
    return this.then(undefined, onRejected)}
  • 接下来我们执行这行代码:
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('error')
    }, 1000);

})
p.then(value => {
    console.log(111);
}).then(value => {
    console.log(222);
}).then(value => {
    console.log(333);
}).catch(reason => {
    console.log(reason);
})

如果你对Promise有了解的话应该知道上面的代码应该会输出error,没错这个就是catch方法的异常穿透,若其中有一部分发生错误的话,则catch会立马接收到这个信息,利用我们上述刚刚写的catch方法会报错,这是为什么呢?

答案是:当我们p对象还是pending状态的时候会去进行回调函数的保存,console.log(111)这个回调函数返回的是一个pending状态的Promise,且这个回调函数会保存在p这个对象属性上面而且这个失败的回调是一个undefined:因为第二个参数并没有传,状态改完之后会执行reject()函数,则会报错,最简单的来讲失败的原因是因为第二个参数没有传入

1.当然我们内置的Promise.then里面是可以少传参数的,所以我们要进行判断then方法回调函数是否有onRejected参数传入

2.我们同样也要对onResolved函数是否传入进行判断,若没有进行判断的话,则就无法进行then方法的值的传递,所以我们同样的也要用同样的方法进行判断

Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    // 判断回调函数参数
    if (typeof onRejected !== 'function') {  //1.+++++++++++
        onRejected = reason => {
            throw reason;
        }
    }
    if (typeof onResolved !== 'function') { //2.++++++++++
        onResolved = value => value;
        //value => {return value}
    }
    return new Promise((resolve, reject) => {
        //封装函数
        function callback(type) {
            try {
                // 获取回调函数的执行结果
                let result = type(self.PromiseResult);
                //判断
                if (result instanceof Promise) {
                    //如果是Promise类型的对象
                    result.then(v => {
                        resolve(v);
                    }, r => {
                        reject(r);
                    })
                } else {
                    //结果的对象状态为成功
                    resolve(result);
                }
            } catch (e) {
                reject(e);
            }
        }
        if (this.PromiseState === 'fulfilled') {
            callback(onResolved);
        }
        if (this.PromiseState === 'rejected') {
            callback(onRejected);
        }
        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved: function () {
                    callback(onResolved);
                },
                onRejected: function () {
                    callback(onRejected);
                }
            })
        }

    })
}

用我们写好的catch方法去执行下来代码的结果为:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('okok');
    }, 1000);

})
p.then().then(value => {
    console.log(222);
    throw 'error';
}).then(value => {
    console.log(333);
}).catch(reason => {
    console.warn(reason);
})

7CSRpT.png

!!这样我们的catch方法就完成啦!!是不是很简单(没有啦还挺绕的自己重新再写一遍还是有点困难)

第四步:resolve方法封装

  1. 我们的resolve方法不属于实例对象上面的方法,而是属于Promise这个函数对象的方法,所以我们添加应该下面这样声明,并且传递value参数,返回一个Promise对象
  2. 这里我们也要再次判断传入的这个参数是否是一个Promise对象,如果是的话则可以直接调用then方法,不是的话则返回一个Promise对象给它
Promise.resolve = function(value){
    // 返回Promise对象
    return new Promise((resolve,reject) => {
       if(value instanceof Promise){
        value.then(v => {
            resolve(v);
        },r => {
            reject(r);
        })
       } else{
           //状态设置为成功
            resolve(value);
       }
    });
}

执行下列代码进行演示:

let p = Promise.resolve('ok');
const p2 = Promise.resolve(new Promise((resolve, reject) => {
    resolve('success');
}));
console.log(p);
console.log(p2);

结果为:

7CFapq.png

!!这样我们的resolve方法就完成啦!!

第五步:reject方法封装

这个方法和resolve相似这里就不再进一步解释啦

//添加reject方法
Promise.reject = function (reason) {
    return new Promise((resolve,reject) => {
        reject(reason);
    });
}

第六步:all方法封装

我们all方法,返回的结果是一个Promise对象,参数通常是传入一个数组,结果由promise这个的数组状态决定,若数组里面的Promise都成功则返回结果是一个成功的状态,以及数组的一个结果,若其中一个失败的话,返回的结果是一个Promise失败的对象且返回的是数组里面失败的Promise的结果

let p1 = new Promise((resolve, reject) => {
    resolve('Ok');
})
let p2 = Promise.resolve('Success');
let p3 =Promise.resolve('123');
let result = Promise.all([p1,p2,p3]);
console.log(result)

下列为内置Promise的结果:

7CA2y6.png

返回结果的状态是根据数组里面的Promise去判断的

如何判断都成功呢?

1.所以我们要用到遍历,去遍历传进来的这个数组promises,并且每个对象都可以调用then方法

2.我们需要遍历完数组去判断是否都为成功,遍历完数组才能去调用resolve函数,而不是遍历完一个promise[i]就去调用,这样子的话假如第一个成功的话就直接返回了。所以我们需要声明一个变量去计算成功的个数,并且成功状态加1,等遍历完数组进行一个判断:如果这个count的值与传进来的数组长度一样,则代表所有的promise都为成功,再去调用resolve函数返回一个成功的对象

3.我们all方法调用完需要有一个数组去保存结果,所以我们在这要声明一个数组去保存结果,若成功的话则会把结果存到数组,所以我们在成功回调编写代码,我们可以用push方法去保存,但这就有一个瑕疵了,结果的顺序和数组的顺序有可能不相同的,因为先后改变的时间状态不一定,所以这里就是一个很巧妙的地方,见下列代码!!(很重要!!)arr[i] = v这行,我们利用元素在数组的下标对数据进行保存

4.失败的结果就很简单啦,若有一个promise为失败则直接调用reject就可以返回一个失败的promise了

//添加all方法
Promise.all = function (promises) {
    //返回为Promise对象
    return new Promise((resolve, reject) => {
        //声明变量
        let count = 0;  //2.
        let arr = [];
        //遍历
        for (let i = 0; i < promises.length; i++) {   //1.
            //
            promises[i].then(v => {
                //得知对象的状态是成功
                // resolve(); 不能这么写,需要遍历完数组再去调用该函数
                count++;  //2.
                //将当前promise对象成功的结果 存入到数组中
                //arr.push(v); 这种方法会有瑕疵!!看上面分析的原因
                arr[i] = v; //很巧妙的地方

                //做一个判断
                if (count === promises.length) {   //2.
                    resolve(arr);
                }
            }, r => {
                reject(r);
            })
        }
    })
}

!!这样我们的all方法就封装好啦!!可以看下运行结果!

7CmCUU.png

第七步:race封装

我们的race方法呢,传入一个参数也是promise组成的数组,返回结果也是一个Promise对象,而这个对象的状态是由数组里面最先改变状态的promise状态,我们用内置的Promise看一下下面代码运行结果,p1先改变状态所以作为返回结果并且值为ok

let p1 = new Promise((resolve, reject) => {
    resolve('Ok');
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('123');
let result = Promise.race([p1, p2, p3]);
console.log(result)

7CmWIU.png

实现这个方法同样也是需要遍历数组,race与all方法不一样,不需要遍历完整个数组再去判断是否为成功,谁先运行则直接返回该状态就好了,不知道大家能不能理解。race封装稍微简单一些

//添加race方法
Promise.race = function(promises){
    return new Promise((resolve,reject) => {
        for(let i = 0; i < promises.length ; i++){
            promises[i].then(v => {
                //修改返回对象的状态为成功
                resolve(v);
            },r => {
                //修改返回对象的状态为失败
                reject(r);
            })
        }
    })
}
  • 同样我们来验证自己写的代码是否正确

执行下列代码:

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Ok');
    }, 1000);

})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('123');
let result = Promise.race([p1, p2, p3]);
console.log(result)
  • 由于p1是经过一秒才改变状态,所以Promise.all返回的应该是p2的结果,我们来看下结果

7CnZWQ.png

最后一个步骤了!!!快大功告成

我们都知道Promise.then是一个异步执行任务而且是一个微任务,这可能牵扯到一点eventloop知识了,这里就不详细说eventloop知识啦,来举一个例子,我们用内置的Promise来运行下面代码,则控制台会输出111–>333–>222,因为会等同步代码运行完后才会去执行异步代码,用我们自己写的Promise,控制台则会输出111–>222–>333。

let p1 = new Promise((resolve, reject) => {
    resolve('Ok');
    console.log('111');
})
p1.then(value => {
    console.log('222');
})
console.log(333);

我们需要怎么做才能让我们then是异步执行的呢,我们在then方法里面进行更改,我们在回调函数外面包一个settimeout函数使其变成异步任务(当然底层肯定不是这样实现的啦!!)只用看末尾有++++号的就是新增代码

function Promise(executor) {
    //添加属性,一开始的状态为pending
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // 声明属性
    this.callbacks = [];
    //保存实例对象的this值
    const self = this;
    //resolve 函数
    function resolve(data) {
        // 判断状态(解决状态可逆)
        if (self.PromiseState !== 'pending') return
        //修改对象状态(promiseState)
        self.PromiseState = 'fulfilled';
        //设置对象结果值(promiseResult)
        self.PromiseResult = data;
        //调用成功的回调函数
        setTimeout(() => {      //++++++++
            self.callbacks.forEach(item => {
                item.onResolved(data)
            })
        });

    }
    //reject 函数
    function reject(data) {
        // 判断状态(解决状态可逆)
        if (self.PromiseState !== 'pending') return
        //修改对象状态(promiseState)
        self.PromiseState = 'rejected';
        //设置对象结果值(promiseResult)
        self.PromiseResult = data;
        //调用失败的回调函数
        setTimeout(() => {      //++++++++
            self.callbacks.forEach(item => {
                item.onRejected(data)
            })
        });

    }
    try {
        //同步调用执行器函数,执行resolve和reject
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}
//给原型添加then方法,这样实例对象可以用then方法
Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    // 判断回调函数参数
    if (typeof onRejected !== 'function') {
        onRejected = reason => {
            throw reason;
        }
    }
    if (typeof onResolved !== 'function') {
        onResolved = value => value;
        //value => {return value}
    }
    return new Promise((resolve, reject) => {
        //封装函数
        function callback(type) {
            try {
                // 获取回调函数的执行结果
                let result = type(self.PromiseResult);
                //判断
                if (result instanceof Promise) {
                    //如果是Promise类型的对象
                    result.then(v => {
                        resolve(v);
                    }, r => {
                        reject(r);
                    })
                } else {
                    //结果的对象状态为成功
                    resolve(result);
                }
            } catch (e) {
                reject(e);
            }
        }
        if (this.PromiseState === 'fulfilled') {
            setTimeout(() => {     //++++++++
                callback(onResolved);
            });

        }
        if (this.PromiseState === 'rejected') {
            setTimeout(() => {     //++++++++
                callback(onRejected);
            });
        }
        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved: function () {
                    callback(onResolved);
                },
                onRejected: function () {
                    callback(onRejected);
                }
            })
        }

    })
}

总结

本文还蛮长的,整理也整理了好久,当然面试的话通常不会让你手写一个完整的Promise,比较重要的是Promise.all和Promise.race方法这两个比较常考手写题!本人自己虽然写了两三遍Promise题但感觉还是蛮复杂的,要考虑的点蛮多的,所以一定要多敲代码!多敲代码!多敲代码!!重要的事情说三遍!如果喜欢的话可以点赞收藏,本人也是第一次写这么长的总结文章,如果有哪写的不好的地方,请大家谅解!有问题的话可以再评论区提出!后面附上手写Promise的源码放在github上面,有ES5版本的还有ES6用class写的版本


文章作者: Daniel Lin
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Daniel Lin !
  目录