美文网首页
Promise的实现?

Promise的实现?

作者: 小小小小的人头 | 来源:发表于2019-04-08 11:02 被阅读0次

    今天实现以下如何实现promise的实现。为什么出现这个点。大家肯定都知道。就是所谓的'回掉地狱'。就是函数里面不断嵌套函数。当多了的话,不便于 维护代码以及修改一处到处处爆炸。

    日常使用吧

    //使用
    const promise = new Promise((resolve, reject) => {
           // 异步处理
           // 处理结束后、调用resolve 或 reject
    });
    //取值
    promise.then(function(data){
    console.log("data") //这样就可以拿到data异步处理的值了
    })
    

    上面代码 写过的小伙伴再熟悉不过了。Promise 是一个构造函数, new Promise 返回一个 promise对象 。异步处理的结果都在这个promise对象里面。我们可以通过 .then()方式拿到值。

    面试知识点?

    promise相当于一个状态机。promise里面有三种状态pending(等待)、fulfilled(满足)、rejected(不合格);

    • promise初始化时候里面的状态是pending
    • 当调用了函数里面resolve方法时候。状态更改完fulfilled
    • 当调用了函数里面reject方法时候。状态更改完rejected

    注意promsie状态 只能由 pending => fulfilled/rejected, 一旦修改就不能再变

    去写下代码吧

    function Promise(executor){
        let self = this;
        self.status = 'pending';  //初始化状态为pending
        self.value = null; //成功状态时候里面的返回值
        self.reason = null;//失败时候里面的返回值
        //resolve 或者reject 方法 改变内部状态以及返回值
        function resolve(value){ 
            if( self.status === 'pending'){ 
                self.status = 'fulfilled';
                self.value = value;
            }
        }
        function reject(reason){ 
            if( self.status === 'pending'){
                self.status = 'rejected';
                self.reason = reason;
            }
        }
        executor(resolve,reject);
    }
    Promise.prototype.then = function(onFulfilled,onRejected){
    
        let self = this;//self就是返回的promise的对象啦,里面有状态以及value
        if(self.status === 'fulfilled'){
            onFulfilled(self.value);
        }
        if(self.status === 'rejected'){
            onRejected(self.reason);
        }
    }
    

    上面代码就实现了简单的promise的功能了。我们下面可以执行下下面的测试demo

    let promise = new Promise(function(resolve,reject){
        resolve("返回值吧");
    })
    console.log("promise==>",promise);//Promise{status:'fulfilled',value:'返回值','reason:null'}
    promise.then((data)=>{
    console.log("data==>",data); //返回值吧
    })
    

    和我们日常使用是不是一样的效果啦。不过这个仅仅是实现了处理同步的情况,当遇到异步情况时候。 控制台是什么都不输出的。那自然不行。异步的例子在下面可以复制进行测试。那我们继续往下写吧;
    代码中有+号 代表 需要在原基础上添加的代码。可以清楚知道添加了什么

    //第二版
    function Promise(executor){ 
        let self = this;
        self.status = 'pending'; 
        self.value = undefined;
        self.reason = undefined;
      + self.onResolvedCallbacks = [];
      + self.onRejectedCallbacks = [];
        
        function resolve(value){
            if( self.status === 'pending'){
                self.status = 'fulfilled'; 
                self.value = value; 
              + self.onResolvedCallbacks.forEach(function(fn){
                    fn();
                })
            }
        }
        
        function reject(reason){
            if( self.status === 'pending'){//只能从pending状态切换到rejected状态
                self.status = 'rejected';
                self.reason = reason;
              + self.onRejectedCallbacks.forEach(function(fn){
                    fn();
                })
            }
        }
        executor(resolve,reject);
    }
    
    Promise.prototype.then = function(onFulfilled,onRejected){
        let self = this;
        if(self.status === 'fulfilled'){
            onFulfilled(self.value);
        }
        if(self.status === 'rejected'){
            onRejected(self.reason);
        }
       
     + if(self.status === 'pending'){
            self.onResolvedCallbacks.push( function(){
                onFulfilled(self.value)
            });
            self.onRejectedCallbacks.push( function(){
                onRejected(self.reason)
            });
        }
    }
    

    好了代码添加完成。下面我们测试下用例。忘了说这个例子是 支持多次then的调用的~

    //测试用例2
    let promise = new Promise(function(resolve, reject) {
        setTimeout(()=>{
            resolve("zx");
        },1000)
     
    });
    promise.then(
      function(data) {
        console.log(data);
      },
      function(err) {
        console.log(err);
      }
    );
    promise.then(
      function(data) {
        console.log(data);
      },
      function(err) {
        console.log(err);
      }
    );
    //控制台输出  zx  zx
    

    上面红色字体说了支持多次调用,但我们平常开发中除了多了调用。还有一个使用方式就是 链式调用 上面的实现就再不支持;当你写链式调用时候 控制台只会告诉你 Cannot read property 'then' of undefined 那继续改良代码吧
    下面继续写代码 代码中+ 表示后加的代码(只修改then方法就可以了)

    Promise.prototype.then = function (onFulfilled, onRejected) {
        const self = this;
     +  let promise2;    // 定义一个promise2变量
    
        if (self.status === 'fulfilled') {
             // 返回一个新的promise
     +      promise2 = new Promise(function (resolve, reject) {
                // x可能是个普通值,也可能是个promise
     +          let x = onFulfilled(self.value);
                // x也可能是别人写的promise,写一个函数统一去处理
     +          resolvePromise(promise2, x, resolve, reject);
            });
        }
        if (self.status === 'rejected') {
     +       promise2 = new Promise(function (resolve, reject) {
     +          let x = onRejected(self.reason);
     +          resolvePromise(promise2, x, resolve, reject);
            });
        }
        // 当调用then时 可能没成功也没失败,就处于pending状态
        if (self.status === 'pending') {
            // 将成功的回调添加到数组中
    +        promise2 = new Promise(function (resolve, reject) {
                self.onFulfilledCb.push(function () {
    +               let x = onFulfilled(self.value);
    +               resolvePromise(promise2, x, resolve, reject);
                });
    
                self.onRejectedCb.push(function () {
    +               let x = onRejected(self.reason);
    +               resolvePromise(promise2, x, resolve, reject);
                });
            });
        }
    
        return promise2;
    };
    function resolvePromise(p2, x, resolve, reject) {
        if (p2 === x) {    // 不能返回自己
            return reject(new TypeError('循环引用'));
        }
        let called;     // 表示是否调用成功or失败
        // x返回的可能是对象和函数也可能是一个普通的值
        if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
            try {
                let then = x.then;
    
                if (typeof then === 'function') {
                    then.call(x, function (y) {
                        // 防止多次调用
                        if (called) return;
                        called = true;
                        // y可能还是个promise,所以递归继续解析直到返回一个普通值
                        resolvePromise(p2, y, resolve, reject);
                    }, function (e) {
                        if (called) return;
                        called = true;
                        reject(e);
                    });
                } else {    
                    // 处理then不是函数的情况,如{then: 1},就直接返回成功
                    resolve(x);
                }
            } catch(e) {
                if (called) return;
                called = true;
                reject(e);
            }
        } else {
            resolve(x);     // 返回一个普通值
        }
    }
    

    下面贴一下测试用例 看下结果集

    let promise = new Promise(function(resolve,reject){
      setTimeout(()=>{
        resolve("zx")
      },1000)
    })
    promise.then(function(data){
       console.log(data)
        return 333;
    }).then((data1)=>{
      console.log(data1)
        // return data1;
    }
    

    下面流程注意点看一下

    • 如果then中无论是成功的回调还是失败的回调有return返回值,都会走下一个then的成功
    • 如果第一个promise返回了一个普通值,会进入下一次then的成功回调;如果第一个promise返回了一个新的promise,需要等待返回promise执行的结果传递给下一次的then中

    以上我们已经实现了promise的链式调用了,但这还不够promise有一种情况是在then中什么都不传的情况,还继续链式调用

    promise.then().then().then(function (data) {
        console.log(data);  
    },
    

    代码再加一点点改良 就可以了

    Promise.prototype.then = function(onFulfilled, onRejected) {
    +    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
            return value
        };
    +    onRejected = typeof onRejected === 'function' ? onFulfilled : function (err) {
            throw err
        };  
        const self = this;
        let promise2;
    };
    

    本来想学习到这里就结束了。 突然想到我们平常开发中 还有一个promise.all的API。既然用到了 那就也实现以下吧。要是没有用到过的小伙伴。看下下面的例子,就知道大概是什么了

    let p = new Promise(function(resolve, reject) {
       setTimeout(function() {
            resolve('北京');
        }, 1000);
    });
    let p2 = new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve('南京');
        }, 200);
    });
    let p3 = new Promise(function(resolve, reject) {
        resolve('东京');
    });
    Promise.all([p, p2, p3]).then(function(data) {
        // all方法是所有的promise都成功后才会成功
        // 按照传入all里的顺序依次执行,p里面的定时器只是执行完成的时间而已,不影响all里输出顺序
        // 如:p这个需要等1秒后才会返回成功态,p2就需要等待1秒时间
        console.log(data);    // [ '北京', '南京', '东京' ]
    });
    
    

    知道了大概的效果后。我们就直接写吧

     Promise.all = function(items) {
        return new Promise(function(resolve, reject) {
            let res = [];    // 用来存储成功函数返回的值
            let num = 0;  // 记录都返回成功的数字
            let len = items.length;  // 数组的长度 
            // 对数组进行遍历
            for (let i = 0; i < len; i++) {
                  items[i].then(function(data) {
                      res[i] = data;
                      if (++num === len) {
                          resolve(res);
                      }
                  }, reject);
            }
        });
    };
    

    代码实现完了 ,其实很简单吧。 就是我们函数里面接收一个新数组,数组里面存放的是3个promise对象。然后再用一个数组接收他的返回值;

    啰嗦一句:不过我在日常开发中我还是不太喜欢用这个的。 因为结果集是返回数组的,你需要处理这些数据都是通过下标去处理对应的,我感觉太麻烦了~ 不喜欢用

    好了。今天的学习就到这边了 依旧贴出大佬的原创地址 支持原创~

    相关文章

      网友评论

          本文标题:Promise的实现?

          本文链接:https://www.haomeiwen.com/subject/pcldiqtx.html