JavaScript进阶之手写Promise

4 篇文章 0 订阅

前言


Promise异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。这里手写一次,希望能和大家一起彻底掌握Promise。

概述


所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

Promise对象有以下两个特点:

  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

上图摘自MDN

Promise的具体用法可以参考阮一峰老师的 《ECMAScript 6 入门》MDN

这里有几点需要注意:

  1. Promise 新建之后就会立即执行
  2. Promise 状态已经变成resolved,再抛出错误是无效的。
  3. 一般来说,调用resolvereject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolvereject的后面。所以,最好在它们前面加上return语句,这样就不会有意外。
  4. 建议省略then中的第二个参数,并在then之后使用catch捕获异常,这是因为这样写不但可以捕获之前的异常,还可以捕获当前then抛出的异常

开始


有了上面的简介及promise的特征,我们来一步步实现Promise,为了和es6的Promise区分,我们使用JPromise来做类名。

  1. 构造函数只有一个executor函数最为参数,并且立即执行了 ,executor函数有两个回调函数:resolve和reject
    class JPromise{
        constructor(executor){
            function resolve(value){

            }
            function reject(error){

            }
            try {
                executor(resolve,reject);
            } catch (error) {
                console.log(error);
            }
        }
    }

     2. 定义promise的三种状态,执行resolve时,状态由pending变为fulfill,执行reject时,状态由pending变为rejecting。then中根据当前实例的状态执行对应的方法。

    /* 
    * 定义状态常量
    */
    const PENDING = 1;
    const FULFILLED = 2;
    const REJECTING = 3;

    /* 
    * 定义JPromise构造函数,定义then原型方法
    * @params {Function} excutor;函数参数依次是resolve和reject
    * 
    * @return {Object};返回promise实例对象
    */
    class JPromise{
        constructor(excutor){
            const me = this;
            me.status = PENDING;
            me.value = null;
            me.error = null;

            function resolve(val){
                if(me.status === PENDING){//检查状态,不可逆操作
                    me.status = FULFILLED;
                    me.value = val;
                }
            }

            function reject(val){
                if(me.status === PENDING){//检查状态,不可逆操作
                    me.status = REJECTING;
                    me.error = val;
                }
            }

            try {
                excutor(resolve,reject);
            } catch (error) {
                reject(error);
            }
        }

        
        then(onResolve,onReject){
            const me = this;
            if(me.status === FULFILLED){
                onResolve(me.value);
            }
            if(me.status === REJECTING){
                onReject(me.error);
            }
        }
    }

执行测试代码:

    new JPromise((resolve)=>{
        resolve(1);
    }).then((res)=>{
        console.log(res);
    });

返回结果:

     3. 如果我们的resolve或者reject回调函数中有异步操作,那么上面的代码就不会返回我们预期的结果了,这时,我们要开始添加异步处理:

第一步:我们先定义两个实例数组属性(resolveCallback和rejectCallback)用来存储then的两个回调函数(使用数组是应为一个promise实例可能使用一次或者多次then方法)

第二步:在调用then方法的时候,如果状态还未改变,我们就向resolveCallback、rejectCallback数组中增加then参数对应的方法;如果状态已经改变,我们就直接使用当前结果传入then方法的参数方法中并执行。

第三步:Promise中的resolve或reject函数执行时,触发resolveCallback或者rejectCallback中的函数。就实现异步调用then中的方法了。

    /* 
    * 同步JPromise的功能添加异步执行功能
    */

    /* 
    * 定义状态常量
    */
    const PENDING = 1;
    const FULFILLED = 2;
    const REJECTING = 3;

    class JPromise{
        constructor(excutor){
            const me = this;
            me.status = PENDING;
            me.value = null;
            me.error = null;
            me.resolveCallback = [];//以数组存储多个then的回调函数
            me.rejectCallback = [];//以数组存储多个then的回调函数

            function resolve(val){
                if(me.status === PENDING){//检查状态,不可逆操作
                    me.status = FULFILLED;
                    me.value = val;
                    me.resolveCallback.forEach(func => func(val));//状态改变为fulfilled并执行resolve
                }
            }

            function reject(val){
                if(me.status === PENDING){//检查状态,不可逆操作
                    me.status = REJECTING;
                    me.error = val;
                    me.rejectCallback.forEach(func => func(val));//状态改变为rejecting并执行reject
                }
            }

            try {
                excutor(resolve,reject);
            } catch (error) {
                reject(error);
            }
        }

        then(onResolve,onReject){
            const me = this;
            onResolve = typeof onResolve === 'function'
                ? onResolve
                : v => v;
            onReject = typeof onReject === 'function'
                ? onReject
                : e => {
                    throw e;
                }
            if(me.status === PENDING){//状态未改变,存储回调不执行
                me.resolveCallback.push(onResolve);
                me.rejectCallback.push(onReject);
            }else if(me.status === FULFILLED){
                setTimeout(() => {//保证then是异步执行的
                    try {
                        onResolve(me.value);
                    } catch (error) {
                        onReject(error);
                    }
                });
            }else if(me.status === REJECTING){
                setTimeout(() => {//保证then是异步执行的
                    try {
                        onReject(me.error);
                    } catch (error) {
                        onReject(error);
                    }
                });
            }
        }

    }

执行测试代码:

    new JPromise((resolve,reject) => {
        console.log('before resolve');
        setTimeout(() => {
            resolve('resolved');
        },1000);
        console.log('after resolve');
    }).then(res =>{
        console.log(res);
    });

    //运行结果:

    //before resolve
    //after resolve
    //resolved (延迟1秒后运行)

    var p = new JPromise((resolve,reject) => {
        setTimeout(() => {
            resolve('delay 2000s');
            p.then(res => {
                console.log(`run after resolve,res is:  ${res}`);
            });
        },2000);
    });

    p.then(res => {
        console.log(res);
    });
    p.then(res => {
        console.log(`${res} run again`);
    });
    //运行结果:

    //2秒后:
    //delay 2000s
    //delay 2000s run again
    //run after resolve,res is:  delay 2000s

执行结果和预期的一样。

     4. 实现then的链式操作,resolve、reject可传入promise实例作为参数

在then中返回一个新的promise,如果状态还未改变,则在改该promise的构造函数中执行存储回调函数;否则立即执行回调函数。catch和finally函数都是then函数的调用

    /* 
    * 定义状态常量
    */
    const PENDING = 1;
    const FULFILLED = 2;
    const REJECTING = 3;

    class JPromise{
        constructor(excutor){
            const me = this;
            me.status = PENDING;
            me.value = null;
            me.error = null;
            me.resolveCallback = [];//以数组存储多个then的回调函数
            me.rejectCallback = [];//以数组存储多个then的回调函数

            function resolve(val){
                if(val instanceof JPromise){//resolve传入promise对象
                    return val.then(resolve,reject);
                }
                if(me.status === PENDING){//检查状态,不可逆操作
                    me.status = FULFILLED;
                    me.value = val;
                    me.resolveCallback.forEach(func => func(val));//状态改变为fulfilled并执行resolve
                }
            }

            function reject(val){
                if(val instanceof JPromise){//resolve传入promise对象
                    return val.then(resolve,reject);
                }
                if(me.status === PENDING){//检查状态,不可逆操作
                    me.status = REJECTING;
                    me.error = val;
                    me.rejectCallback.forEach(func => func(val));//状态改变为rejecting并执行reject
                }
            }

            try {
                excutor(resolve,reject);
            } catch (error) {
                reject(error);
            }
        }

        /* 
        * 链式操作不是返回this,而是返回一个新的promise实例
        */
        then(onResolve,onReject){
            const me = this;
            onResolve = typeof onResolve === 'function'
                ? onResolve
                : v => v;
            onReject = typeof onReject === 'function'
                ? onReject
                : e => {
                    throw e;
                }
            if(me.status === PENDING){
                return new JPromise((resolve,reject) => {//返回新的实例
                    me.resolveCallback.push(() => {
                        try {
                            resolvePromise(onResolve(me.value),resolve,reject);
                        } catch (error) {
                            reject(error);
                        }
                    });
                    me.rejectCallback.push(() => {
                        try {
                            resolvePromise(onReject(me.error),resolve,reject);
                        } catch (error) {
                            reject(error);
                        }
                    });
                });
            }else if(me.status === FULFILLED){
                return new JPromise((resolve,reject) => {//返回新的实例
                    setTimeout(() => {
                        try {
                            resolvePromise(onResolve(me.value),resolve,reject);
                        } catch (error) {
                            reject(error);
                        }
                    });
                });
            }else if(me.status === REJECTING){
                return new JPromise((resolve,reject) => {//返回新的实例
                    setTimeout(() => {
                        try {
                            onReject(me.error);
                        } catch (error) {
                            reject(error);
                        }
                    });
                });
            }
        }

        /* 
        * 捕获promise抛出错误或者reject状态改变返回值
        */
        catch(onReject){
            return this.then(null,onReject);
        }

        /* 
        * 总是最后执行
        */
        finally(callback){
            return this.then(callback,callback);
        }
    }

    function resolvePromise(retValue,resolve,reject){
        if(retValue instanceof JPromise){//resolve或reject传入promise实例
            if(retValue.status === PENDING){
                retValue.then(ret => {
                    resolvePromise(ret,resolve,reject);
                },error => {
                    reject(error);
                });
            }else{
                retValue.then(resolve,reject);
            }
        }else{
            resolve(retValue);
        }
    }

执行测试代码:

    var p = new JPromise((resolve,reject) => {
        setTimeout(() => {
            resolve('1000ms delay--p');
        },1000);
    }).then(res => {
        console.log(res);
        return new JPromise((resolve,reject) => {
            setTimeout(() => {
                console.log('run after 2000ms--p');
                resolve('2000ms delay--p');
            },1000);
        });
    }).then(res => {
        console.log(res);
    });

    //运行结果:
    //1000ms delay--p (1s 后执行)
    //run after 2000ms--p (2s 后执行)
    //2000ms delay--p (2s 后执行)

    var p1 = new JPromise((resolve,reject) => {
        setTimeout(() => {
            console.log('1000ms delay--p1');
            return resolve(new JPromise((rs,rj) => {
                setTimeout(() => {
                    return rs('delay 2000ms--p1');
                },1000);
            }));
        },1000);
    }).then(res => {
        console.log(res);
    });

    //运行结果:
    //1000ms delay--p1 (1s后执行)
    //delay 2000ms--p1 (2s 后执行)

    var p2 = new JPromise((resolve,reject) => {
        reject(1);
    }).then(res => {
        console.log(res);
        throw new Error('error');
    },err => {
        console.log('then catch error');
        throw new Error('inner error');
    }).catch(err => {
        console.log(err);
    });

    //运行结果:

    //then catch error
    //inner error

    var p3 = new JPromise((resolve,reject) => {
        setTimeout(() => {
            reject(1);
        },1000);
    }).catch(err => {
        console.log(err); 
        return new JPromise((rs,rj) => {
            setTimeout(() => {
                rs(2);
            },1000);
        });
    }).then(res => {
        console.log(res); 
        return 'foo';
    }).finally(s => {
        console.log(s); 
        console.log('finally run');
    });

    //运行结果:

    //1 (1s后)
    //2 (2s后)
    //foo (2s后)
    //finally run (2s后)

执行结果:

     5. 实现全局方法all和race

all是传入一组promise实例,直至所有实例状态都变为fulfill状态则执行then中的resolve,参数为所有实例resolve传入的值组成的数组,否则执行reject;实现原理为为数组中的每一个实例代理添加一个then方法,方法中定义一个计数器,当计数器和实例数组长度相等时,改变返回promise的状态,继续实现链式操作

race接收一组promise实例,当只有某一个实例率先状态改变为fulfill时,执行resolve,参数为该promise的resolve传入的值;改变为reject时执行reject。race的实现就比较简单了,只要数组中的一个实例状态改变,则改变新返回的实例状态。

/* 
    * 定义状态常量
    */
    const PENDING = 1;
    const FULFILLED = 2;
    const REJECTING = 3;

    class JPromise{
        constructor(excutor){
            const me = this;
            me.status = PENDING;
            me.value = null;
            me.error = null;
            me.resolveCallback = [];//以数组存储多个then的回调函数
            me.rejectCallback = [];//以数组存储多个then的回调函数

            function resolve(val){
                if(val instanceof JPromise){//resolve返回promise对象
                    return val.then(resolve,reject);
                }
                if(me.status === PENDING){//检查状态,不可逆操作
                    me.status = FULFILLED;
                    me.value = val;
                    me.resolveCallback.forEach(func => func(val));//状态改变为fulfilled并执行resolve
                }
            }

            function reject(val){
                if(me.status === PENDING){//检查状态,不可逆操作
                    me.status = REJECTING;
                    me.error = val;
                    me.rejectCallback.forEach(func => func(val));//状态改变为rejecting并执行reject
                }
            }

            try {
                excutor(resolve,reject);
            } catch (error) {
                reject(error);
            }
        }

        /* 
        * 链式操作不是返回this,而是返回一个新的promise实例
        */
        then(onResolve,onReject){
            const me = this;
            onResolve = typeof onResolve === 'function'
                ? onResolve
                : v => v;
            onReject = typeof onReject === 'function'
                ? onReject
                : e => {
                    throw e;
                }
            if(me.status === PENDING){
                return new JPromise((resolve,reject) => {//返回新的实例
                    me.resolveCallback.push(() => {
                        try {
                            resolvePromise(onResolve(me.value),resolve,reject);
                        } catch (error) {
                            reject(error);
                        }
                    });
                    me.rejectCallback.push(() => {
                        try {
                            resolvePromise(onReject(me.error),resolve,reject);
                        } catch (error) {
                            reject(error);
                        }
                    });
                });
            }else if(me.status === FULFILLED){
                return new JPromise((resolve,reject) => {//返回新的实例
                    setTimeout(() => {
                        try {
                            resolvePromise(onResolve(me.value),resolve,reject);
                        } catch (error) {
                            reject(error);
                        }
                    });
                });
            }else if(me.status === REJECTING){
                return new JPromise((resolve,reject) => {//返回新的实例
                    setTimeout(() => {
                        try {
                            onReject(me.error);
                        } catch (error) {
                            reject(error);
                        }
                    });
                });
            }
        }

        /* 
        * 捕获promise抛出错误或者reject状态改变返回值
        */
        catch(onReject){
            return this.then(null,onReject);
        }

        /* 
        * 总是最后执行
        */
        finally(callback){
            return this.then(callback,callback);
        }

        static resolve(value){
            return value instanceof JPromise 
                ? value
                : new JPromise(resolve => {
                    return resolve(value);
                });
        }

        static reject(error){
            return value instanceof JPromise 
                ? value
                : new JPromise(null,reject => {
                    return reject(error);
                });
        }

        static all(promises){
            promises = Array.isArray(promises) ? promises : [promises];
            return new JPromise((resolve,reject) => {
                const length = promises.length;
                let values = [];//返回结果组成的数组
                let counter = 0;

                promises.forEach((singlePromise,index) => {
                    if(!(singlePromise instanceof JPromise)){//如果不是promise实例则调用resolve全局方法
                        singlePromise = JPromise.resolve(singlePromise);
                    }
                    singlePromise.then(ret => {
                        values[index] = ret;//保证返回结果按照传入的数组顺序来
                        if(++counter === length){//所有promise都执行resolve时,返回的promise执行resolve
                            resolve(values);
                        }
                    },reject);
                });
            });
        }

        static race(promises){
            promises = Array.isArray(promises) ? promises : [promises];

            return new JPromise((resolve,reject) => {
                if(!(singlePromise instanceof JPromise)){//如果不是promise实例则调用resolve全局方法
                    singlePromise = JPromise.resolve(singlePromise);
                }
                promises.forEach(singlePromise => {
                    singlePromise.then(resolve,reject);//执行第一个状态改变的promise,并返回该值到新返回的promise的resolve
                })
            });
        }

    }

    function resolvePromise(retValue,resolve,reject){
        if(retValue instanceof JPromise){
            if(retValue.status === PENDING){
                retValue.then(ret => {
                    resolvePromise(ret,resolve,reject);
                },error => {
                    reject(error);
                });
            }else{
                retValue.then(resolve,reject);
            }
        }else{
            resolve(retValue);
        }
    }

测试代码:


    var p1 = new JPromise((resolve,reject) => {
        setTimeout(() => {
            resolve(1);
        },2000);
    });
    var p2 = new JPromise((resolve,reject) => {
        setTimeout(() => {
            resolve(2);
        },1000);
    });

    JPromise.all([p1,p2,'aa']).then(res => {
        console.log(res);
    });

    //运行结果:

    //[1,2,'aa'] (2s 后)

    var p3 = new JPromise((resolve,reject) => {
        setTimeout(() => {
            reject('some error')
        },2000);
    });

    var p4 = new JPromise((resolve,reject) => {
        setTimeout(() => {
            resolve(1);
        },1000);
    });

    JPromise.all([p3,p4]).then(res => {
        cosnole.log(res);
    },error => {
        console.log('rejected');
    });

    //运行结果:

    //rejected (2s 后)

    var p5 = new JPromise((resolve,reject) => {
        setTimeout(() => {
            resolve(1);
        },2000);
    });

    var p6 = new JPromise((resolve,reject) => {
        setTimeout(() => {
            resolve(2);
        },1000);
    });

    var p7 = new JPromise((resolve,reject) => {
        setTimeout(() => {
            reject('someErrors');
        },1500);
    });

    JPromise.race([p5,p6,p7]).then(res => {
        console.log(res);
    },error => {
        console.log(error);
    });

    //运行结果:

    //2 (1s 后)

运行结果:

至此,一个比较规范的promise实现了,申明:以上代码使用es6风格代码,均可改成es5。代码都上传到GitHub

 

参考


《ECMAScript 6 入门》

《ES6 系列之我们来聊聊 Promise 》

《八段代码彻底掌握 Promise》

《Promise原理讲解 && 实现一个Promise对象 》

《Promise》

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值