手动实现个promise

记录下所学的,来检验自己的掌握程度
试着手动实现个promise

let promise1 = new Promise((resolve, reject)=>{
    setTimeout(() => {
        resolve('张三');
    }, 2000);
})
promise1
.then(res=>{
    console.log(res);   //  两秒后打印出了 '张三'
},err=>{
    console.log(err);
});

//  来实现这样的一个函数
/*
    注意的点:
    1 状态只能 pending -> resolved pending -> rejected
    2 从上边可以看出 then 函数是在 原型上边
*/
function MyPromise(exeuctor){
    self.status = 'pending';
    self.value = '';
    self.reason = '';

    //  改变为成功的状态
    function resolve(data){
        self.value = data;
        self.status = 'resolved';
    }

    //  改变为失败的状态
    function reject(reason){
        self.reason = reason;
        self.status = 'rejected';
    }

    try {
        exeuctor(resolve, reject)
    } catch (err) {
        //  执行异步函数出错 直接改变为失败状态
        reject(err);
    }
}

MyPromise.prototype.then = function(onresolve, onrejected){
    if(this.status == 'resolved'){
        onresolve(self.value);
    }
    if(this.status == 'rejected'){
        onrejected(self.reason);
    }
}

let myPromise1 = new MyPromise((resolve,reject)=>{
    setTimeout(() => {
        resolve('李四');
    }, 2000);
})

myPromise1.then((data)=>{
    console.log(data);
},err=>{
    console.log(err);
})

李四 并没有被打印出来 
分析原因得知 函数并没有 先执行 resolve('李四') 然后执行 onresolve 打印出 李四
先执行了 then中的函数 2s后执行 resolve('李四') 顺序反了
怎样才能 在执行 resolve('李四') 函数后,立即执行 then 注册的函数?
调用then的时候 MyPromise 处于 pending的状态 可以在这个时候 存储then注册的函数
状态改变的时候 再来执行
来实现

function MyPromise(exeuctor){
    let self = this;
    self.status = 'pending';
    self.value = '';
    self.reason = '';
+   self.resolveCallbacks = [];
+   self.rejectCallbacks = [];

    //  改变为成功的状态
    function resolve(data){
        self.value = data;
        self.status = 'resolved';
+       self.resolveCallbacks.forEach(func => func(data));
    }

    //  改变为失败的状态
    function reject(reason){
        self.reason = reason;
        self.status = 'rejected';
 +      self.rejectCallbacks.forEach(func => func(reason));
    }

    try {
        exeuctor(resolve, reject)
    } catch (err) {
        //  执行异步函数出错 直接改变为失败状态
        reject(err);
    }
}

MyPromise.prototype.then = function(onresolve, onrejected){
    if(this.status == 'resolved'){
        onresolve(this.value);
    }
    if(this.status == 'rejected'){
        onrejected(this.reason);
    }
  + if(this.status == 'pending'){
  +     this.resolveCallbacks.push(onresolve);
  +     this.rejectCallbacks.push(onrejected);
  + }
}

这样就成功的两秒后打印出了李四
在 pendding的时候 注册函数 在状态改变的时候 有个好听的名字 叫订阅发布

接下来就是令人头疼的 链式调用的实现了
链式调用 比如:promise1.then().then();
在调用then的时候需要返回一个 promise
也就是说每一次调用then 都等于 创建了一个 promise对象

先来实现一个简单的链式调用
思路就是 then 函数返回一个 promise
想实现这样一个简单的链式调用
改造then函数

MyPromise.prototype.then = function(onresolve, onrejected){
    let promise2;
    promise2 = new MyPromise((resolve, reject)=>{
        if(this.status == 'resolved'){
        // 为什么要加setTimeout?
        // 首先是promiseA+规范要求的
        // 其次是大家写的代码,有的是同步,有的是异步
        // 所以为了更加统一,就使用为setTimeout变为异步了,保持一致性
            setTimeout(() => {
                try {
                    let x = onresolve(self.value);
                    resolvePromise(promise2,x,resolve,reject);    
                } catch (error) {
                    reject(error)
                }
            }, 0);
        }
        if(this.status == 'rejected'){
            setTimeout(() => {
                try {
                    let x = onrejected(self.reason);
                    resolvePromise(promise2,x,resolve,reject);
                } catch (error) {
                    reject(error)
                }
            }, 0);
        }
        if(this.status == 'pending'){
            this.resolveCallbacks.push(()=>{
                //  当第一个promise状态改变的时候 执行then注册的函数
                //  并改变当前的 promise 的状态 就实现了链式调用
                setTimeout(()=>{
                    try{
                        let x =  onresolve(self.value)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            });
            this.rejectCallbacks.push(()=>{
                setTimeout(()=>{
                    try{
                        let x = onrejected(self.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            });
        }
    });
    return promise2;
}

来测试下
let myPromise1 = new MyPromise((resolve,reject)=>{
    setTimeout(() => {
        resolve('李四');
    }, 2000);
})

myPromise1
.then((data)=>{
        console.log(data+'1');
    },err=>{
        console.log(err);
    })
.then((data)=>{
        console.log(data+'2');
    },err=>{
        console.log(err);
    })
.then((data)=>{
        console.log(data+'3');
    },err=>{
        console.log(err);
    })
复制代码

// 依次打印出了 李四1 李四2 李四3

实现个复杂的链式调用
当 let x = onresolve(this.value); 中的x为一个promise时
这时候就需要一个函数 来确定 x 是不是 promise

        if(promise2 === x){
            return reject(new TypeError('Chaining cycle detected for promise'))
        }

        let called
        if(x !== null && (typeof x === 'object' || typeof x === 'function')){
            try{
                let then = x.then 
                
                if(typeof then === 'function'){
                    then.call(x,(y)=>{ //
                        if(called) return
                        called = true
                        
                        //  promise2 resolve reject 三个参数一直都是不变的
                        //  迭代出最后一个promise
                        resolvePromise(promise2,y,resolve,reject)
                    },(err)=>{ 
                        if(called) return
                        called = true
                        reject(err)
                    })
                }else{
                    resolve(x) 
                }
            }catch(e){ 
                if(called) return
                called = true
                reject(e)
            }
        }else{ 
            //  当then 注册的函数返回是一个常量的时候 就决议 promise
            //  最后一个promise
            resolve(x)
        }
    }
复制代码

对我来说,其中最难理解的还是,resolvePormise 对返回是promise的处理
如果返回的是promise 就注册then函数,并使用 resolvePormise 处理
这样就处理了整个链式调用

完整代码,顺便测试下

    function MyPromise(exeuctor){
    let self = this;
    self.status = 'pending';
    self.value = undefined;
    self.reason = undefined;
    self.resolveCallbacks = [];
    self.rejectCallbacks = [];

    //  改变为成功的状态
    function resolve(data){
        if(self.status == 'pending'){
            self.value = data;
            self.status = 'resolved';
            self.resolveCallbacks.forEach(func => func());
        }
    }

    //  改变为失败的状态
    function reject(reason){
        if(self.status == 'pending'){
            self.reason = reason;
            self.status = 'rejected';
            self.rejectCallbacks.forEach(func => func());
        }
    }

    try {
        exeuctor(resolve, reject)
    } catch (err) {
        //  执行异步函数出错 直接改变为失败状态
        reject(err);
    }
}

function resolvePromise(promise2,x,resolve,reject){
    if(promise2 === x){
        return reject(new TypeError('Chaining cycle'));
    }
    let called; // 防止状态改变多次
    if(x!==null&&(typeof x === 'object' || typeof x === 'function')){
        try {
            let then = x.then;
            if(typeof then === 'function'){    // x 为 promise
                //  重写了then函数 再次注册了 then函数 
                then.call(x,y => {
                    if(called) return;
                    called = true;
                    resolvePromise(promise2,y,resolve,reject);
                },err=>{
                    if(called) return;
                    called = true;
                    reject(err)
                })
            } else {
                resolve(x);
            }
        } catch (err) {
            if(called) return;
            called = true;
            reject(err);
        }
    } else {
        /*
            如果x是一个常量 就决议当前的promise 调用then注册的函数
        */  
        resolve(x);
    }
}

MyPromise.prototype.then = function(onresolve, onrejected){
    let promise2;
    let self = this;
    onresolve = typeof onresolve === 'function'?onresolve:val=>val;
    onrejected = typeof onrejected === 'function'?onrejected: err=>{throw err}
    promise2 = new MyPromise((resolve, reject)=>{
        if(self.status === 'resolved'){
            setTimeout(() => {
                try {
                    let x = onresolve(self.value);
                    resolvePromise(promise2,x,resolve,reject);    
                } catch (error) {
                    reject(error)
                }
            }, 0);
        }
        if(self.status === 'rejected'){
            setTimeout(() => {
                try {
                    let x = onrejected(self.reason);
                    resolvePromise(promise2,x,resolve,reject);
                } catch (error) {
                    reject(error)
                }
            }, 0);
        }
        if(self.status === 'pending'){
            self.resolveCallbacks.push(()=>{
                // 同13
                setTimeout(()=>{
                    try{
                        let x =  onresolve(self.value)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            }) 
            self.rejectCallbacks.push(()=>{
                // 同13
                setTimeout(()=>{
                    try{
                        let x = onrejected(self.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            })
        }
    });
    return promise2;
}
MyPromise.defer = MyPromise.deferred = function(){
    let dfd = {};
    dfd.promise = new MyPromise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}
module.exports = MyPromise;
复制代码

接下来我们要安装一个插件,npm install promises-aplus-test -g

WechatIMG536.png

写出来了一个符合 promiseA+规范的myPromise,很开心,
非常推荐阅读
小邵教你玩转promise源码
我的这个记录也是来源于这篇文章;
接着来实现两个函数
race 和 all
相信大家都知道这两个函数的作用,不说这两个函数

MyPromise.race = function(promises){
        return new MyPromise((resolve, reject)=>{
            promises.forEach(item=>{
                item.then(resolve,reject);
            })
        })
    }
    //  测试
    let raceRes = MyPromise.race([
        new MyPromise((resolve,reject)=>{
            setTimeout(() => {
                resolve(2000)
            }, 2000)
        }),
        new MyPromise((resolve,reject)=>{
            setTimeout(() => {
                resolve(5000)
            }, 5000)
        })
    ])
    .then(res=>{
        console.log(res);  // 2000
    },err=>{
        console.log(err);
    })
    
    ----------------all-----------------
    MyPromise.all = function(promises){
        return new MyPromise((resolve, reject)=>{
            let resArr = [];
            let i = 0;
            function processData(key,res){
                resArr[key] = res;
                if(++i === promises.length){
                    resolve(resArr)
                }
            }

            promises.forEach((item,key)=>{
                item.then(res=>{
                    processData(key,res);
                },err=>reject(err))
            })
        })
    };
    MyPromise.all([
        new MyPromise((resolve,reject)=>{
            setTimeout(() => {
                resolve(2000)
            }, 2000)
        }),
        new MyPromise((resolve,reject)=>{
            setTimeout(() => {
                resolve(5000)   
            }, 5000)
        })
    ])
    .then(res=>{
        console.log('all',res); //  all (2) [2000, 5000]
    },err=>{
        console.log(err);
    })
复制代码

非常推荐阅读
小邵教你玩转promise源码

记录下所学,水平有限难免错误,欢迎指出 ~~

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享