博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
手写一款符合Promise/A+规范的Promise
阅读量:5770 次
发布时间:2019-06-18

本文共 24486 字,大约阅读时间需要 81 分钟。

手写一款符合Promise/A+规范的Promise

长篇预警!有点长,可以选择性观看。如果对Promise源码不是很清楚,还是推荐从头看,相信你认真从头看到尾,并且去实际操作了,肯定会有收获的。主要是代码部分有点多,不过好多都是重复的,不必担心

Promise的一些用法在此不多赘述,本篇主要带领你手写一个Promise源码,学完你就会发现:Promise没有你想象中的那么难

本篇大概分为以下步骤

  • 实现简单的同步Promise
  • 增加异步功能
  • 增加链式调用then
  • 增加catch finally方法
  • 增加all race 等方法
  • 实现一个promise的延迟对象defer
  • 最终测试

实现简单的同步Promise

先大概说一下基本概念:

Promise内部维护着三种状态,即pending,resolved和rejected。初始状态是pending,状态可以有pending--->relolved,或者pending--->rejected.不能从resolve转换为rejected 或者从rejected转换成resolved.
即 只要Promise由pending状态转换为其他状态后,状态就不可变更。
ok.知道了这些后,我们开始手撸代码:

注意观看序号 1 2 3 4 5 ...

function Promise(executor){    let that = this;    /** 2 定义初始的一些变量 */    that.status = 'pending';    that.value = null;    that.reason = null;        /** 3 定义初始的成功和失败函数 */    function resolve(value){        /** 4 判断状态是不是初始状态pending          *    是就转换状态 否则不转换          *    确保状态的变化后的不可变性 */        if(that.status === 'pending'){            that.status = 'resolved';            that.value = value;        }    }    function reject(reason){        /** 5 同上 */        if(that.status === 'pending'){            that.status = 'rejected';            that.reason = reason;        }    }    /**     * 1 Promise中首先传了一个executor,它是一个函数     *   executor函数中又传了两个函数,分别是resolve和reject     *   很显然 resolve是成功回调,reject是失败的回调     */    executor(resolve,reject);}/** 6 在Promise原型上面定义then方法 *    then方法上面有两个回调 一个是成功后的方法 另一个是失败后的方法 *    根据成功或失败的状态去执行相关成功onFilfulled()或者失败onRejected()的回调方法 */Promise.prototype.then = function(onFilfulled,onRejected){    let that = this;    if(that.status === 'resolved'){        /** 7 如果状态已经变更为resolved          *    说明resolve方法已经被调用         *    那么此时就执行成功的回调函数onFilfulled         *    并传入参数 that.value         * */        onFilfulled(that.value);    }    if(that.status === 'rejected'){        /** 8 同上         *    传入参数 that.reason         */        onRejected(that.reason);    }}module.exports = Promise;

通过require()引入手撸的Promise

let Promise = require('./myPromise');let p1 = ()=>{    return new Promise((resolve,reject)=>{        resolve('success.1');    });}p1().then((data)=>{    console.log(data); // 打印 success.1},(err)=>{    console.log(err);});

ok.经调用发现 此代码可以实现部分Promise的功能,但仅仅是同步下才有效果。

那异步呢? 别急这就来~:

增加异步功能

注意观看序号 1 2 3 4 5 ...

function Promise(executor){    let that = this;    that.status = 'pending';    that.value = null;    that.reason = null;    /** 1 因为异步不是立即执行 状态不会变更 成功或失败的回调函数也不会执行     *    所以先定义好存放成功或失败回调函数的数组      *    以便将成功或失败的回调函数先保存起来     * */    that.onFilFulledCallbacks = [];    that.onRejectedCallbacks = [];    function resolve(value){        if(that.status === 'pending'){            that.status = 'resolved';            that.value = value;            /** 3 发布             *    等待状态发生变更             *    状态变更后 立即执行之前存放在相应数组中所有的成功或失败的回调函数             *    即 发布             */            that.onFilFulledCallbacks.forEach((fn)=>{                fn();            });        }    }    function reject(reason){        if(that.status === 'pending'){            that.status = 'rejected';            that.reason = reason;            /** 4 同上 */            that.onRejectedCallbacks.forEach((fn)=>{                fn();            });        }    }    executor(resolve,reject);}Promise.prototype.then = function(onFilfulled,onRejected){    let that = this;    if(that.status === 'resolved'){        onFilfulled(that.value);    }    if(that.status === 'rejected'){        onRejected(that.reason);    }    /** 2 订阅     *    因为是异步 状态当时并没有立即变更 所以状态还是pending     *    此时需要把成功或者失败的回调函数存放到对应的数组中     *    等待状态变更时 再从数组中拿出来去执行     *    即 订阅     *    *存放数组时 为了执行时方便 需要把回调函数的外层包裹一层空函数     */    if(that.status === 'pending'){        that.onFilFulledCallbacks.push(function(){            onFilfulled(that.value);        });    }    if(that.status === 'pending'){        that.onRejectedCallbacks.push(function(){            onRejected(that.reason);        });    }}module.exports = Promise;

代码测试:

let Promise = require('./myPromise');let p1 = ()=>{    return new Promise((resolve,reject)=>{        setTimeout(function(){            resolve('success.1');            // reject('fail.');        },1500);    });}p1().then((data)=>{    console.log(data); // success.1},(err)=>{    console.log(err);});

可以看到 1.5s后 执行了resolve() 并打印了success.1,至此,我们实现了异步的Promise.其实这里的实现异步的思想就是发布订阅.

en~ok.高能预警?.接下来就稍稍复杂了 因为我们要实现链式调用then。 要实现这个功能那我们就要重写then方法,并在then方法中重新返回一个Promise,只有这样,才可以实现多次调用then.而且要新增一个解析返回值是否为promise的函数.

稍微捋下逻辑:

  • 如果一个then方法返回一个普通值的话,这个值会传递给下一个then中作为resolve成功的结果
  • 如果一个then方法返回一个promise的话,会根据返回的promise是成功还是失败,决定下一个then是成功还是失败

? 上代码:

增加链式调用then

注意观看序号 1 2 3 4 5 ...

function Promise(executor){    let that = this;    that.status = 'pending';    that.value = null;    that.reason = null;    that.onFilFulledCallbacks = [];    that.onRejectedCallbacks = [];    function resolve(value){        if(that.status === 'pending'){            that.status = 'resolved';            that.value = value;            that.onFilFulledCallbacks.forEach((fn)=>{                fn();            });        }    }    function reject(reason){        if(that.status === 'pending'){            that.status = 'rejected';            that.reason = reason;            that.onRejectedCallbacks.forEach((fn)=>{                fn();            });        }    }    executor(resolve,reject);}Promise.prototype.then = function(onFilfulled,onRejected){    let that = this;    /** 1 让promise2等于一个新的Promise 并将promise2返回 */    let promise2 = new Promise((resolve,reject)=>{        if(that.status === 'resolved'){            /** 2 因为返回了promise2              *  并且第3步resolvePromiseRelation函数中传递了promise2             *  而目前promise2并没有拿到             *  所以加一个定时器 异步执行 等到promise2拿到后              *  再去执行 resolvePromiseRelation()方法 并将promise2传递进去*/            setTimeout(()=>{                try{                    let promise3 = onFilfulled(that.value);                    /** 3 判断新返回值是什么类型的函数                     *  并将当前的promise:promise2  新的返回值:promise3                      *  和 成功时回调:esolve  失败时回调:reject 作为参数传进去 */                    resolvePromiseRelation(promise2,promise3,resolve,reject);                }catch(e){                    reject(e);                }            },0);        }        if(that.status === 'rejected'){            /** 同2 */            setTimeout(()=>{                try{                    let promise3 = onRejected(that.reason);                    /** 同3*/                    resolvePromiseRelation(promise2,promise3,resolve,reject);                }catch(e){                    reject(e);                }            },0);        }        if(that.status === 'pending'){            that.onFilFulledCallbacks.push(function(){                /** 同2 */                setTimeout(()=>{                    try{                        let promise3 = onFilfulled(that.value);                        /** 同3*/                        resolvePromiseRelation(promise2,promise3,resolve,reject);                    }catch(e){                        reject(e);                    }                },0);            });        }        if(that.status === 'pending'){            that.onRejectedCallbacks.push(function(){                 /** 同2 */                 setTimeout(()=>{                    try{                        let promise3 = onRejected(that.reason);                        /** 同3*/                        resolvePromiseRelation(promise2,promise3,resolve,reject);                    }catch(e){                        reject(e);                    }                },0);            });        }    });    /** 同1 */    return promise2;}function resolvePromiseRelation(promise2,promise3,resolve,reject){     /** 4 防止自己等待自己 一直循环等待 */    if(promise2 === promise3){        return reject(new TypeError('循环引用了!'));    }    /**  8 一个标示 表示当前没有被调用过      *   确保resolve或者reject后的状态不会再次发生变更    */     let called;     /** 5 保证promise3是一个引用类型      *  判断新返回值promise3的类型       *  如果是普通值常量 就直接resolve导出 */    if(promise3!==null&&(typeof promise3 === 'object'||typeof promise3 === 'function')){        try{            let then = promise3.then;            /** 6 确保promise3是一个Promise             *  判断promise3的then方法             *  如果存在 并且是一个function类型              *  就表示promise3是一个Promise */            if(typeof then === 'function'){                /** 9 执行promise3的then方法                 *  因为promise3也是一个Promise                 *  需要再次解析promise3的then方法                 *  直到解析到最后的返回值不是一个Promise类型为止                 */                then(promise3, (promise4)=>{                    /** 同8 */                    if(called) return;                    called = true;                    /** 10 递归解析新的返回值的类型                     *  解析到返回值不是一个Promise类型为止                     */                    resolvePromiseRelation(promise3,promise4,resolve,reject);                },(r)=>{                    /** 同8 */                    if(called) return;                    called = true;                    reject(r);                });            }else{                /** 7 此时promise3是一个普通对象 直接resolve() */                resolve(promise3);            }        }catch(e){            /** 同8 */            if(called) return;            called = true;            reject(e);        };    }else{        /** 同5 普通值直接resolve()*/        resolve(promise3);    }}module.exports = Promise;

ok. 至此 我们已经实现了Promsie的异步和链式调用. Promise中比较复杂的部分我们已经搞定了 接下来就是添加一些方法,其实这部分反而没那么复杂了.

catch : catch方法本质上就是一个then方法的变形,只有失败时的回调 没有成功时的回调

finally : finally方法的作用是不管 Promise 对象最后状态如何,都会执行操作.其实说白了就是在then方法的成功和失败的回调函数中都执行该方法就行了.

ok.老规矩 上代码~

增加catch finally方法
function Promise(executor){    let that = this;    that.status = 'pending';    that.value = null;    that.reason = null;    that.onFilFulledCallbacks = [];    that.onRejectedCallbacks = [];    function resolve(value){        if(that.status === 'pending'){            that.status = 'resolved';            that.value = value;            that.onFilFulledCallbacks.forEach((fn)=>{                fn();            });        }    }    function reject(reason){        if(that.status === 'pending'){            that.status = 'rejected';            that.reason = reason;            that.onRejectedCallbacks.forEach((fn)=>{                fn();            });        }    }    executor(resolve,reject);}Promise.prototype.then = function(onFilfulled,onRejected){    /** 2 此处有个坑 如果只写1 不写2的话     *  会报一个TypeError :onRejected is not a function     *  在此处给它一个默认的成功和失败的回调函数就好 */    onFilfulled = typeof onFilfulled === 'function'?onFilfulled:value=>value;    onRejected = typeof onRejected ===  'function'?onRejected:err=>{throw err};    let that = this;    let promise2 = new Promise((resolve,reject)=>{        if(that.status === 'resolved'){            setTimeout(()=>{                try{                    let promise3 = onFilfulled(that.value);                    resolvePromiseRelation(promise2,promise3,resolve,reject);                }catch(e){                    reject(e);                }            },0);        }        if(that.status === 'rejected'){            setTimeout(()=>{                try{                    let promise3 = onRejected(that.reason);                    resolvePromiseRelation(promise2,promise3,resolve,reject);                }catch(e){                    reject(e);                }            },0);        }        if(that.status === 'pending'){            that.onFilFulledCallbacks.push(function(){                setTimeout(()=>{                    try{                        let promise3 = onFilfulled(that.value);                        resolvePromiseRelation(promise2,promise3,resolve,reject);                    }catch(e){                        reject(e);                    }                },0);            });        }        if(that.status === 'pending'){            that.onRejectedCallbacks.push(function(){                 setTimeout(()=>{                    try{                        let promise3 = onRejected(that.reason);                        resolvePromiseRelation(promise2,promise3,resolve,reject);                    }catch(e){                        reject(e);                    }                },0);            });        }    });    return promise2;}function resolvePromiseRelation(promise2,promise3,resolve,reject){    if(promise2 === promise3){        return reject(new TypeError('循环引用了!'));    }    let called;    if(promise3!==null&&(typeof promise3 === 'object'||typeof promise3 === 'function')){        try{            let then = promise3.then;            if(typeof then === 'function'){                then(promise3, (promise4)=>{                    if(called) return;                    called = true;                    resolvePromiseRelation(promise3,promise4,resolve,reject);                },(r)=>{                    if(called) return;                    called = true;                    reject(r);                });            }else{                resolve(promise3);            }        }catch(e){            if(called) return;            called = true;            reject(e);        };    }else{        resolve(promise3);    }}/** 1 直接返回this的then方法 *  因为catch只捕获错误 所以resolve直接为null *  返回reject就好*/Promise.prototype.catch = function(errFn){    return this.then(null,errFn);}/** 3 finally实现起来也很简单  *  分别在resolve和reject中执行fn就好  *  最后再把this返回出去就好*/Promise.prototype.finally = function(fn){    this.then(()=>{        fn();    },()=>{        fn();    });    return this;}module.exports = Promise;
增加all race resolve reject等方法
function Promise(executor){    let that = this;    that.status = 'pending';    that.value = null;    that.reason = null;    that.onFilFulledCallbacks = [];    that.onRejectedCallbacks = [];    function resolve(value){        if(that.status === 'pending'){            that.status = 'resolved';            that.value = value;            that.onFilFulledCallbacks.forEach((fn)=>{                fn();            });        }    }    function reject(reason){        if(that.status === 'pending'){            that.status = 'rejected';            that.reason = reason;            that.onRejectedCallbacks.forEach((fn)=>{                fn();            });        }    }    executor(resolve,reject);}Promise.prototype.then = function(onFilfulled,onRejected){    onFilfulled = typeof onFilfulled === 'function'?onFilfulled:value=>value;    onRejected = typeof onRejected ===  'function'?onRejected:err=>{throw err};    let that = this;    let promise2 = new Promise((resolve,reject)=>{        if(that.status === 'resolved'){            setTimeout(()=>{                try{                    let promise3 = onFilfulled(that.value);                    resolvePromiseRelation(promise2,promise3,resolve,reject);                }catch(e){                    reject(e);                }            },0);        }        if(that.status === 'rejected'){            setTimeout(()=>{                try{                    let promise3 = onRejected(that.reason);                    resolvePromiseRelation(promise2,promise3,resolve,reject);                }catch(e){                    reject(e);                }            },0);        }        if(that.status === 'pending'){            that.onFilFulledCallbacks.push(function(){                setTimeout(()=>{                    try{                        let promise3 = onFilfulled(that.value);                        resolvePromiseRelation(promise2,promise3,resolve,reject);                    }catch(e){                        reject(e);                    }                },0);            });        }        if(that.status === 'pending'){            that.onRejectedCallbacks.push(function(){                 setTimeout(()=>{                    try{                        let promise3 = onRejected(that.reason);                        resolvePromiseRelation(promise2,promise3,resolve,reject);                    }catch(e){                        reject(e);                    }                },0);            });        }    });    return promise2;}function resolvePromiseRelation(promise2,promise3,resolve,reject){    if(promise2 === promise3){        return reject(new TypeError('循环引用了!'));    }    let called;    if(promise3!==null&&(typeof promise3 === 'object'||typeof promise3 === 'function')){        try{            let then = promise3.then;            if(typeof then === 'function'){                then(promise3, (promise4)=>{                    if(called) return;                    called = true;                    resolvePromiseRelation(promise3,promise4,resolve,reject);                },(r)=>{                    if(called) return;                    called = true;                    reject(r);                });            }else{                resolve(promise3);            }        }catch(e){            if(called) return;            called = true;            reject(e);        };    }else{        resolve(promise3);    }}Promise.prototype.catch = function(errFn){    return this.then(null,errFn);}Promise.prototype.finally = function(fn){    this.then(()=>{        fn();    },()=>{        fn();    });    return this;}/** 1 直接在构造函数上增加all方法 *  它返回的也是一个Promise *  等待参数数组中所有的promise都执行完毕后 *  再返回结果 */Promise.all = function(values){    return new Promise((resolve,reject)=>{        /** 2 定义一个存放最终结果的数组result和一个index */         let results = [];        let index = 0;        /** 3 定义一个方法addToArr()           *  让index每次执行增加results数组元素的函数的时候都+1         *  当index === values的长度的时候 说明此时所有promsie都执行完毕并放到的数组中         *  然后直接resolve(results)就行了        */        function addToArr(key,value){            index++;            results[key] = value;            /** 6 当满足条件时 说明所有的promise都执行完毕 直接resolve(results) */            if(index === values.length){                resolve(results);            }        }        /** 4 循环values中的每一项promsie */        for(let i = 0; i < values.length; i++){            let current = values[i];            /** 5 判断每一项promise的返回值是不是一个Promsie             *  是的话就执行该Promise的then方法 拿到返回值 并放到数组results中             *  是一个普通值的话就直接将该值放到数组results中             */            if(current && current.then && typeof current.then === 'function'){                current.then((value)=>{                    /** 同5 把返回值放到数组results中*/                    addToArr(i,value);                },reject);            }else{                /** 同5 把返回值放到数组results中*/                addToArr(i,current);            }        }    });}/** race方法相比较于all方法简单很多 *  因为race中的promsie成功resolve一个  *  整个race就resolve */Promise.race = function(values){    return new Promise((resolve,reject)=>{        /** 同4 */        for(let i = 0; i < values.length; i++){            let current = values[i];            /** 同5 */            if(current&&current.then&&typeof current.then === 'function'){                /** 7 直接执行then就好 */                current.then(resolve,reject);            }else{                /** 8 普通值直接resolve */                resolve(current);            }        }    });}// resolve方法Promise.resolve = function(value){    return new Promise((resolve,reject)=>{        resolve(value);    });}// reject方法Promise.reject = function(reason){    return new Promise((resolve,reject)=>{        reject(reason);    });}module.exports = Promise;
实现一个promise的延迟对象defer

此步是为了测试我们手写的Promsie符不符合Promsie/A+规范,如果没有defer的话,我们在测试过程中就会报一个TypeError: adapter.deferred is not a function.

其实写完defer后,我们就可以去进行测试我们手写的Promsie符不符合Promsie/A+规范了。

即:本篇手写一款符合Promise/A+规范的Promise的最终本为:

function Promise(executor){    let that = this;    that.status = 'pending';    that.value = null;    that.reason = null;    that.onFilFulledCallbacks = [];    that.onRejectedCallbacks = [];    function resolve(value){        if(that.status === 'pending'){            that.status = 'resolved';            that.value = value;            that.onFilFulledCallbacks.forEach((fn)=>{                fn();            });        }    }    function reject(reason){        if(that.status === 'pending'){            that.status = 'rejected';            that.reason = reason;            that.onRejectedCallbacks.forEach((fn)=>{                fn();            });        }    }    try{        executor(resolve,reject);    }catch(e){        reject(e);    }}Promise.prototype.then = function(onFilfulled,onRejected){    onFilfulled = typeof onFilfulled === 'function'?onFilfulled:value=>value;    onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err};    let that = this;    let promise2 = new Promise((resolve,reject)=>{        if(that.status === 'resolved'){            setTimeout(()=>{                try{                    let promise3 = onFilfulled(that.value);                    resolvePromiseRelation(promise2,promise3,resolve,reject);                }catch(e){                    reject(e);                }            },0);        }        if(that.status === 'rejected'){            setTimeout(()=>{                try{                    let promise3 = onRejected(that.reason);                    resolvePromiseRelation(promise2,promise3,resolve,reject);                }catch(e){                    reject(e);                }            },0);        }        if(that.status === 'pending'){            that.onFilFulledCallbacks.push(function(){                setTimeout(()=>{                    try{                        let promise3 = onFilfulled(that.value);                        resolvePromiseRelation(promise2,promise3,resolve,reject);                    }catch(e){                        reject(e);                    }                },0);            });            that.onRejectedCallbacks.push(function(){                setTimeout(()=>{                   try{                       let promise3 = onRejected(that.reason);                       resolvePromiseRelation(promise2,promise3,resolve,reject);                   }catch(e){                       reject(e);                   }               },0);           });        }    });    return promise2;}function resolvePromiseRelation(promise2,promise3,resolve,reject){    if(promise2 == promise3){        return reject(new TypeError('循环引用了!'));    }    let called;    if(promise3!==null&&(typeof promise3 === 'object' || typeof promise3 === 'function')){        try{            let then = promise3.then;            if(typeof then === 'function'){                then.call(promise3, (promise4)=>{                    if(called) return;                    called = true;                    resolvePromiseRelation(promise3,promise4,resolve,reject);                },(r)=>{                    if(called) return;                    called = true;                    reject(r);                });            }else{                resolve(promise3);            }        }catch(e){            if(called) return;            called = true;            reject(e);        };    }else{        resolve(promise3);    }}Promise.prototype.catch = function(errFn){    return this.then(null,errFn);}Promise.prototype.finally = function(fn){    this.then(()=>{        fn();    },()=>{        fn();    });    return this;}Promise.all = function(values){    return new Promise((resolve,reject)=>{        let results = [];        let index = 0;        function addToArr(key,value){            index++;            results[key] = value;            if(index === values.length){                resolve(results);            }        }        for(let i = 0; i < values.length; i++){            let current = values[i];            if(current && current.then && typeof current.then === 'function'){                current.then((value)=>{                    addToArr(i,value);                },reject);            }else{                addToArr(i,current);            }        }    });}Promise.race = function(values){    return new Promise((resolve,reject)=>{        for(let i = 0; i < values.length; i++){            let current = values[i];            if(current&&current.then&&typeof current.then === 'function'){                current.then(resolve,reject);            }else{                resolve(current);            }        }    });}Promise.resolve = function(value){    return new Promise((resolve,reject)=>{        resolve(value);    });}Promise.reject = function(reason){    return new Promise((resolve,reject)=>{        reject(reason);    });}// 实现一个promise的延迟对象 deferPromise.defer = Promise.deferred = function(){    let dfd = {};    dfd.promise = new Promise((resolve, reject)=>{        dfd.resolve = resolve;        dfd.reject = reject;    });    return dfd;}module.exports = Promise;
最终测试
  • 测试当前代码是否符合Promise/A+规范
  • 全局安装 npm i -g promises-aplus-tests
  • 文件所在目录运行以下命令 (例如你的文件名为:MyPrommise.js)
  • promise-aplus-tests MyPrommise.js
  • 等待
  • ok.

clipboard.png

源码在github上,写文章不易,欢迎star或fork,thx~

转载地址:http://ozsux.baihongyu.com/

你可能感兴趣的文章
Git原理与高级使用(3)
查看>>
从JDK源码看Writer
查看>>
Express 结合 Webpack 实现HMRwi
查看>>
基于protobuf的RPC实现
查看>>
HAProxy负载均衡原理及企业级实例部署haproxy集群
查看>>
Win 8创造颠覆性体验:预览版关键更新
查看>>
JS中比较数字大小
查看>>
jQuery插件的开发
查看>>
基础,基础,还是基础之JAVA基础
查看>>
如何成为一个C++高级程序员
查看>>
我的友情链接
查看>>
显式锁(第十三章)
查看>>
看linux书籍做的一些重要笔记(2011.07.03更新)
查看>>
CString、Char* ,char [20]、wchar_t、unsigned short转化
查看>>
从案例学RxAndroid开发(上)
查看>>
Redis学习手册(内存优化)
查看>>
浅尝TensorFlow on Kubernetes
查看>>
springboot系列十 Spring-Data-Redis
查看>>
excel进行矩阵计算
查看>>
基于Android平台的动态生成控件和动态改变控件位置的方法
查看>>