• 探究Promise的实现


      最终答案在一个类库里,地址 https://github.com/yahoo/ypromise 这个类库也有问题,就是下面这道面试题在IE9里实现不一致,类库里还是用了setTimeout。去年尝试用setTimeout(,0)来实现Promise,见Promise的实现原理 ,最后以失败告终。今天前端leader在群里放了一组面试题,最后一题

    setTimeout(function() {
      console.log(1)
    }, 0);
    new Promise(function executor(resolve) {
      console.log(2);
      for( var i=0 ; i<10000 ; i++ ) {
        i == 9999 && resolve();
      }
      console.log(3);
    }).then(function() {
      console.log(4);
    });
    console.log(5);

    romise 的 then 应当会放到当前 tick 的最后,但是还是在当前 tick 中。因此,应当先输出 5,然后再输出 4 。最后在到下一个 tick,就是 1 。

    “2 3 5 4 1”

    补充解释是最后一题 Promise 的 4 在 1 前面输出是因为 Promise.then()里面的回调属于 microtask, 会在当前 Event Loop 的最后执行, 而 SetTimeout 内的回调属于 macrotask, 会在下一个 Event Loop 中执行。

    这时候忽然发现用setTimeout来实现貌似有问题,然后东哥说当然不能用settimeout实现。promise依赖通知机制,不依赖时间,里面使用了观察者模式。这又给了我新的思路,去

    github上搜一下Promise,有一些写好的库,关键词加上中文二字,也会发现简易实现。迷你书提到了一些较好的Promsie实现,真的有不用settimeout来实现的,好好看一下。

    找到了另外一篇,promise异步编程的原理  担心作者删掉,就在这儿再贴一份(原文有错误,我这儿改正了),也可参考另外一篇。这篇也存在很大的问题,就是在只能用异步的方法,不能用同步。到底去哪找靠谱的实现呢。

    要实现promise对象,首先要考虑几个问题:

    1.promise构造函数中要实现异步对象状态和回调函数的剥离,并且分离之后能够还能使回调函数正常执行

    2.如何实现链式调用并且管理状态

    首先是构造函数:

    //全局宏定义
    var PENDING = 0;
    var FULFILLED = 1;
    var REJECTED = 2;
    //Promise构造函数
    function Promise(fn){
        var self = this;
        self.state = PENDING;//初始化状态
        self.value = null;//存储异步结果的对象变量
        self.handlers = [];//存储回调函数,这里没保存失败回调函数,因为这是一个dome
        //异步任务成功后处理,这不是回调函数
        function fulfill(result){
            if(self.state === PENDING){
                self.state = FULFILLED;
                self.value = result;
                for(var i=0;i<self.handlers.length;i++){
                    self.handlers[i](result);
                }
                
            }
        }
    
        //异步任务失败后的处理,
        function reject(err){
            if(self.state === PENDING){
                self.state = REJECTED;
                self.value = err;
            }
        }   
        fn&&fn(fulfill,reject);
    
    };

    构造函数接受一个异步函数,并且执行这个异步函数,修改promise对象的状态和结果。

    回调函数方法then:

    //使用then方法添加回调函数,把这次回调函数return的结果当做return的promise的resolve的参数
    Promise.prototype.then = function(onResolved, onRejected){
        var self = this;
        return new Promise(function(resolve, reject){
            var onResolvedFade = function(val){
                var ret = onResolved?onResolved(val):val;//这一步主要是then方法中传入的成功回调函数通过return来进行链式传递结果参数
                if(ret instanceof Promise){//回调函数返回值也是promise的时候
                    ret.then(function(val){
                        resolve(val);
                    });
                }
                else{
                    resolve(ret);
                }
            };
            var onRejectedFade = function(val){
                var ret = onRejected?onRejected(val):val;
                reject(ret);
            };
            self.handlers.push(onResolvedFade);
            if(self._status === FULFILLED){
                onResolvedFade(self._value);
            }
    
            if(self._status === REJECTED){
                onRejectedFade(self._value);
            }
        });
    }

    通过上面的代码可以看出,前面提出的2个问题得到了解决,1.在promise对象中有3个属性,state,value,handlers,这3个属性解决了状态和回调的脱离,并且在调用then方法的时候才将回调函数push到handlers属性上面(此时state就是1,可以在后面的代码中执行onResolve)2.链式调用通过在then方法中返回的promise对象实现,并且通过onResolvedFade将上一个回调的返回值当做这次的result参数来执行进行传递。

    测试代码:

    function async(value){
        var pms = new Promise(function(resolve, reject){
            setTimeout(function(){
                resolve(value);;
            }, 1000);
        });
        return pms;
    }
    async(1).then(function(result){
        console.log('the result is ',result);//the result is 2 
        return result;
    }).then(function(result){
        console.log(++result);//2
    });

     

  • 相关阅读:
    Visual Studio 存在版本之间冲突
    Failed to load resoure:the serve responded with a status of 405 (Method Not Allowed)
    Excel 导入解析数据 NPOIExcelHelper
    WIN10 SERVICES -- 部署IIS
    高德地图API-搜索提示并定位到位置,卫星地图和标准地图的切换
    BIMFCE选择全量绘制
    JS中如何获取当前日期,并与输入日期作比较
    html中input标签放入小图标
    div跟随浏览器大小而改变
    vue-amap接入高德地图示例
  • 原文地址:https://www.cnblogs.com/zhansu/p/6530844.html
Copyright © 2020-2023  润新知