从入门到放弃之promise源码解析(下)

460 阅读9分钟

参考文献

new Promise传的参数我们称之为执行器函数(executor),在创建实例的实例会立即执行,看代码

console.log(1);
new Promise((resolve,reject)=>{ 
    console.log(2);
})
console.log(3);   //打印结果1,2,3.说明你在new时候,我们称之为执行器函数就立即执行了

实现promise状态

  • 代码如下
class Promise{
    constructor(executor){
        //默认状态 等待态
        this.status = 'pending';
        // 成功的结果
        this.value = undefined;
        //失败的原因
        this.reason = undefined;
        // 成功态执行函数
        let fulfilled = (value)=>{
            //防止状态改变
            if(this.status ==='pending'){
                this.status ==='resolved' //成功态
                // 成功的结果
                this.value = value;
            }
        };
        //失败态执行函数
        let rejected = (reason)=>{
            //防止状态改变
            if(this.status ==='pending'){
                this.status ==='rejected' //失败态
                // 失败原因
                this.reason = reason;
            }
        };

        // 默认让执行器执行,可能会发生错误
        try{
            executor(fulfilled,rejected);
        }catch (e) {
            rejected(e)
        }

    }
}
module.exports = Promise;

then方法的实现

步骤一、实现同步promise(每个步骤配一个例子来看效果)

class Promise{
    constructor(executor){
        //默认状态 等待态
        this.status = 'pending';
        // 成功的结果
        this.value = undefined;
        //失败的原因
        this.reason = undefined;
        // 成功态执行函数
        let fulfilled = (value)=>{
            if(this.status ==='pending'){
                this.status ='resolved' //成功态
                // 成功的结果
                this.value = value;
            }
        };
        //失败态执行函数
        let rejected = (reason)=>{
            if(this.status ==='pending'){
                this.status ='rejected' //失败态
                // 失败原因
                this.reason = reason;
            }
        };

        // 默认让执行器执行,可能会发生错误
        try{
            executor(fulfilled,rejected);
        }catch (e) {
            rejected(e)
        }

    }
    then(onFulfilled, onRejected) {
        if(this.status ==="resolved"){
            onFulfilled(this.value)
        }
        if(this.status ==="rejected"){
            onRejected(this.reason)
        }
    }
}
module.exports = Promise;

那就来简单测试下

步骤二、有可能我们会往执行器中写一些异步逻辑,这个时候我们该怎么做呢?

了解过发布订阅模式应该知道。我们应该先把回调函数存放数组中,等发布时候在让数组中函数一一调用。修改下代码

class Promise{
    constructor(executor){
        //默认状态 等待态
        this.status = 'pending';
        // 成功的结果
        this.value = undefined;
        //失败的原因
        this.reason = undefined;
        // 成功存放的数组
        this.onResolvedCallbacks = [];
        // 失败存放的数组
        this.onRejectedCallbacks = [];
        // 成功态执行函数
        let fulfilled = (value)=>{
            if(this.status ==='pending'){
                this.status ='resolved' //成功态
                // 成功的结果
                this.value = value;
                //依次执行成功的回调函数
                this.onResolvedCallbacks.forEach(fn=>fn())
            }
        };
        //失败态执行函数
        let rejected = (reason)=>{
            if(this.status ==='pending'){
                this.status ='rejected' //失败态
                // 失败原因
                this.reason = reason;
                //依次执行失败的回调函数
                this.onRejectedCallbacks.forEach(fn=>fn())
            }
        };

        // 默认让执行器执行,可能会发生错误
        try{
            executor(fulfilled,rejected);
        }catch (e) {
            rejected(e)
        }

    }
    then(onFulfilled, onRejected) {
        if(this.status ==="resolved"){
            onFulfilled(this.value)
        }
        if(this.status ==="rejected"){
            onRejected(this.reason)
        }
        //如果等待态(即异步逻辑时)要做一件事情
        if(this.status ==='pending'){
            this.onResolvedCallbacks.push(()=>{
                onFulfilled(this.value);
            })
            this.onRejectedCallbacks.push(()=>{
                onRejected(this.reason);
            })
        }
    }
}
module.exports = Promise;

测试写代码

步骤三、实现穿透,看下原生promise就秒懂什么是穿透啦

let p = new Promise((resolve,reject)=>{ //引用原生的promise
    resolve("买")
});
p.then().then().then().then(data=>{ //可以无限的.then,最终拿到data值。即穿透
    console.log(data);
});

怎么实现的呢,先看下promiseA+规范中定义,然后我们用代码实现它

  • then返回新的promise2
then(onFulfilled, onRejected) {
        let promise2;
        //默认成功和失败不穿参数情况
        onFulfilled = typeof onFulfilled ==="function"? onFulfilled:value=>value;
        onRejected  = typeof onRejected ==="function"? onRejected:err=>{
            throw err;
        };
        promise2 = new Promise((resolve,reject)=>{
            if(this.status ==="resolved"){
               //成功逻辑
               let x = onFulfilled(this.value);
               /* 判断x是不是promise,如果是promise取它结果,作为promise2成功结果
                 *要是返回普通值,作为promise2成功的结果
                 * todo: resolvePromise可以解析x和promise2关系
                 */
                resolvePromise(promise2,x,resolve,reject);
            }
            if(this.status ==="rejected"){
                //失败逻辑
              let x=  onRejected(this.reason);
                resolvePromise(promise2,x,resolve,reject);
            }
            //如果等待态(即异步逻辑时)要做一件事情
            if(this.status ==='pending'){
                this.onResolvedCallbacks.push(()=>{
                  let x =  onFulfilled(this.value);
                    resolvePromise(promise2,x,resolve,reject);
                })
                this.onRejectedCallbacks.push(()=>{
                  let x =  onRejected(this.reason);
                    resolvePromise(promise2,x,resolve,reject);
                })
            }
        })
        //调用then后,返回新的promise2。实现多次then
        return promise2;
    }
  • 解析x和promise2的关系。定义方法 resolvePromise()
// //解析x与promise2的关系
function resolvePromise(promise2,x,resolve,reject) {
    /*判断x是不是promise
    *A+规范规定一段代码,这个代码可以实现我们的promise和别人的promise可以进行交互
     */
    if (promise2 === x) { //自己不能等待自己完成
        return reject(new TypeError('循环引用'));
    }
    // x不是null或者是对象或者函数
    if(x!==null && typeof x === 'object' || typeof x==='function'){
        let called; //标识promise是否被调用过,防止成功后调用失败
        try{ //防止取then出现异常
            let then = x.then; // 取x的then方法
            if(typeof then === 'function'){ //如果then是函数我就认为它是promise
                then.call(x,y=>{ //call第一个参数是this,后面是成功和失败的回调
                    if(called) return
                    called = true;
                    // 如果y是promise就继续递归解析promise
                    resolvePromise(promise2,y,resolve,reject);
                },err=>{ //只要失败就失败
                    if(called) return
                    called = true;
                    reject(err);
                });
            }else {
                //then是一个普通对象
                if(called) return
                called = true;
                resolve(x)
            }
        }catch (e) {
            reject(e)
        }
    }else {
        resolve(x);
    }
}

这样基本的promise就算实现完成了

步骤五优化一些细节,包括try catch的捕获及异步的处理

  • 整理后的then方法就是这样,每句代码都有对应注释,在A+规范可以对应找到
then(onFulfilled, onRejected) {
        let promise2;
        //默认成功和失败不穿参数情况
        onFulfilled = typeof onFulfilled ==="function"? onFulfilled:value=>value;
        onRejected  = typeof onRejected ==="function"? onRejected:err=>{
            throw err;
        };
        promise2 = new Promise((resolve,reject)=>{
            if(this.status ==="resolved"){
                //执行上下文为异步
                setTimeout(()=>{
                    try{
                        //成功逻辑
                        let x = onFulfilled(this.value);
                        /* 判断x是不是promise,如果是promise取它结果,作为promise2成功结果
                          *要是返回普通值,作为promise2成功的结果
                          * todo: resolvePromise可以解析x和promise2关系
                          */
                        resolvePromise(promise2,x,resolve,reject);
                    }catch (e) {
                        reject(e)
                    }
                })
            }
            if(this.status ==="rejected"){
               setTimeout(()=>{
                   try{
                       //失败逻辑
                       let x=  onRejected(this.reason);
                       resolvePromise(promise2,x,resolve,reject);
                   }catch (e) {
                       reject(e)
                   }
               })
            }
            //如果等待态(即异步逻辑时)要做一件事情
            if(this.status ==='pending'){
                this.onResolvedCallbacks.push(()=>{
                 setTimeout(()=>{
                     try{
                         let x =  onFulfilled(this.value);
                         resolvePromise(promise2,x,resolve,reject);
                     }catch (e) {
                         reject(e)
                     }
                 })
                });
                this.onRejectedCallbacks.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x =  onRejected(this.reason);
                            resolvePromise(promise2,x,resolve,reject);

                        }catch (e) {
                            reject(e)
                        }
                    })
                })

            }
        })
        //调用then后,返回新的promise2。实现多次then
        return promise2;
    }

catch用法

  • 接受参数只有失败
  • 只是then没有成功的简写
 catch(onRejected){ //接受参数只有失败
        return this.then(null,onRejected)
    }

Promise.resolve()

  • 基础用法可以看上篇文章,这里就不在解释了,直接写实现原理
  • 返回promise,直接调用成功
Promise.resolve = function (val){ //
    return new Promise((resolve,reject)=>resolve(val)
    )
};

Promise.reject()的实现

  • 返回一个promise
  • 把值传给成功的回调
Promise.reject = function (val){
    return new Promise((resolve,reject)=>reject(val)
    )
};

promise.all()实现

接受一个数组,返回一个promise,处理每个参数的返回结果,有一个失败就失败,全部成功才成功

Promise.all = function(promises){ //返回promise,接受一个数组
    let arr = [];
    let i=0; //i的目的是为了保证获取全部成功,来设置索引
    function processDara(index,data){
        arr[index] = data;
        i++;
        if(i ===promises.length){
            resolve(arr);
        }
    }
    return new Promise((resolve,reject)=>{
       for(let i=0;i<promises.length;i++){
           promises[i].then(data=>{
               //处理数据
                processDara(i,data)
           },err=>{ //有一个失败就失败
               reject(err)
           })
       }
    })
};

Promise.race()实现

接受一个数组,与all不同的是,不用处理数据,有一个成功就成功,有一个失败就失败

Promise.race = function(promises){ //返回promise,接受一个数组
    return new Promise((resolve,reject)=>{
        for(let i=0;i<promises.length;i++){
            promises[i].then(data=>{ //有一个成功就成功
                resolve(data)
            },err=>{ //有一个失败就失败
                reject(err)
            })
        }
    })
};

最后放一个测试promise安装包

// 目前是通过他测试 他会测试一个对象
// 语法糖
Promise.defer = Promise.deferred = function () {
    let dfd = {}
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
}
module.exports = Promise;
// npm install  promises-aplus-tests -g
// promises-aplus-tests  文件名称

测试下我们写的全部代码

附上我们全部代码供大家参考,有不对地方请大家指正

// //解析x与promise2的关系
function resolvePromise(promise2,x,resolve,reject) {
    /*判断x是不是promise
    *A+规范规定一段代码,这个代码可以实现我们的promise和别人的promise可以进行交互
     */
    if (promise2 === x) { //自己不能等待自己完成
        return reject(new TypeError('循环引用'));
    }
    // x不是null或者是对象或者函数
    if(x!==null && (typeof x === 'object' || typeof x==='function')){
        let called; //标识promise是否被调用过,防止成功后调用失败
        try{ //防止取then出现异常
            let then = x.then; // 取x的then方法
            if(typeof then === 'function'){ //如果then是函数我就认为它是promise
                then.call(x,y=>{ //call第一个参数是this,后面是成功和失败的回调
                    if(called) return;
                    called = true;
                    // 如果y是promise就继续递归解析promise
                    resolvePromise(promise2,y,resolve,reject);
                },r=>{ //只要失败就失败
                    if(called) return
                    called = true;
                    reject(r);
                });
            }else {
                resolve(x)
            }
        }catch (e) {
            //then是一个普通对象
            if(called) return
            called = true;
            reject(e)
        }
    }else {
        resolve(x);
    }
}
class Promise{
    constructor(executor){
        //默认状态 等待态
        this.status = 'pending';
        // 成功的结果
        this.value = undefined;
        //失败的原因
        this.reason = undefined;
        // 成功存放的数组
        this.onResolvedCallbacks = [];
        // 失败存放的数组
        this.onRejectedCallbacks = [];
        // 成功态执行函数
        let fulfilled = (value)=>{
            if(this.status ==='pending'){
                this.status ='resolved' //成功态
                // 成功的结果
                this.value = value;
                //依次执行成功的回调函数
                this.onResolvedCallbacks.forEach(fn=>fn())
            }
        };
        //失败态执行函数
        let rejected = (reason)=>{
            if(this.status ==='pending'){
                this.status ='rejected' //失败态
                // 失败原因
                this.reason = reason;
                //依次执行失败的回调函数
                this.onRejectedCallbacks.forEach(fn=>fn())
            }
        };

        // 默认让执行器执行,可能会发生错误
        try{
            executor(fulfilled,rejected);
        }catch (e) {
            rejected(e)
        }

    }
    then(onFulfilled, onRejected) {
        let promise2;
        //默认成功和失败不穿参数情况
        onFulfilled = typeof onFulfilled ==="function"? onFulfilled:value=>value;
        onRejected  = typeof onRejected ==="function"? onRejected:err=>{
            throw err;
        };
        promise2 = new Promise((resolve,reject)=>{
            if(this.status ==="resolved"){
                //执行上下文为异步
                setTimeout(()=>{
                    try{
                        //成功逻辑
                        let x = onFulfilled(this.value);
                        /* 判断x是不是promise,如果是promise取它结果,作为promise2成功结果
                          *要是返回普通值,作为promise2成功的结果
                          * todo: resolvePromise可以解析x和promise2关系
                          */
                        resolvePromise(promise2,x,resolve,reject);
                    }catch (e) {
                        reject(e)
                    }
                })
            }
            if(this.status ==="rejected"){
               setTimeout(()=>{
                   try{
                       //失败逻辑
                       let x=  onRejected(this.reason);
                       resolvePromise(promise2,x,resolve,reject);
                   }catch (e) {
                       reject(e)
                   }
               })
            }
            //如果等待态(即异步逻辑时)要做一件事情
            if(this.status ==='pending'){
                this.onResolvedCallbacks.push(()=>{
                 setTimeout(()=>{
                     try{
                         let x =  onFulfilled(this.value);
                         resolvePromise(promise2,x,resolve,reject);
                     }catch (e) {
                         reject(e)
                     }
                 })
                });
                this.onRejectedCallbacks.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x =  onRejected(this.reason);
                            resolvePromise(promise2,x,resolve,reject);

                        }catch (e) {
                            reject(e)
                        }
                    })
                })

            }
        })
        //调用then后,返回新的promise2。实现多次then
        return promise2;
    }
    catch(onRejected){ //接受参数只有失败
        return this.then(null,onRejected)
    }
}
module.exports = Promise;
Promise.resolve = function (val){
    return new Promise((resolve,reject)=>resolve(val)
    )
};
Promise.reject = function (val){
    return new Promise((resolve,reject)=>reject(val)
    )
};
Promise.all = function(promises){ //返回promise,接受一个数组
    let arr = [];
    let i=0; //i的目的是为了保证获取全部成功,来设置索引
    function processDara(index,data){
        arr[index] = data;
        i++;
        if(i ===promises.length){
            resolve(arr);
        }
    }
    return new Promise((resolve,reject)=>{
       for(let i=0;i<promises.length;i++){
           promises[i].then(data=>{
               //处理数据
                processDara(i,data)
           },err=>{ //有一个失败就失败
               reject(err)
           })
       }
    })
};
Promise.race = function(promises){ //返回promise,接受一个数组
    return new Promise((resolve,reject)=>{
        for(let i=0;i<promises.length;i++){
            promises[i].then(data=>{ //有一个成功就成功
                resolve(data)
            },err=>{ //有一个失败就失败
                reject(err)
            })
        }
    })
};
// 目前是通过他测试 他会测试一个对象
// 语法糖
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
}
module.exports = Promise;
// npm install  promises-aplus-tests -g
// promises-aplus-tests  文件名称