• 说说Q.js中的promise的历史


    转载自:http://segmentfault.com/a/1190000002591145

    Promise核心说明

    promise的规范,请百度搜索Prommise/A+规范,promise的核心是有个then方法。在相关术语中,promise指的就是有个 then 

    方法,且该方法能触发特定行为的对象或函数。

    起步:用这一种方法理解Promise

    回想一下Promise解决的是什么问题?回调。 例如:函数 doMission1() 代表第一件事情,现在,我们想要在这事情完成后,再做下

    一件事情 doMisson2(),应该怎么做呢?

    先看看我们常见的回调模式。 doMission1() 说:“你要这么做的话,就把doMission2()交给我,我在结束后帮你调用。" 所以会是:

    doMission1( doMission2 );

    Promise模式又如何呢?你对 doMission1()说:“不行,控制权要在我这里。你应该要改变一下,你先返回一个特别的东西给我,然

    后我来这里安排下一件事。” 这个特别的东西就是Promise,这会变成这样:

    doMission1().then( doMission2 );

    可以看出,Promise将回调模式的主从关系换了一个位置(翻身做主人!), 多个事件的流程关系,就可以这样集中到主干到上(而不是

    分散在各个事件函数之内)。

    好了,如何做这样一个转换呢? 从最简单的情况来吧,假定 doMission1()的代码是:

     function doMission1(callback){
         var value = 1;
         callback( value );
     }
     

    那么,它可以改变一下,变成这样:

     function doMission1(){
         var value = 1,
             
             self = {
                 then: function( callback ){
                     callback( value );
                 }
             };
             
             return self;
     }

    这就完成了转换。虽然并不是实际有用的转换,但到这里,其实已经触及了Promise最为重要的实现要点

    Promise将返回值转换为带有 then 方法的对象。

     进阶: Q的设计路程

    从def开始

     var def = function(){
         var pending = [], value,
             
             self = {
                 resolve: function( _value ){
                     value = _value;
                     for( var i = 0, len = pending.length; i < len; i++ ){
                         var callback = pending[ i ];
                         callback( value );
                     }
                     pending = undefined;
                 },
                 
                 then: function(){
                     if( pending ){
                         pending.push( callback );
                     }else{
                         callback( value );
                     }
                 }
             };
             
         return self;
     };
     

    这段源码可以看出,运行 def() 将得到一个对象,该对象包含 resolve 和 then 方法。请回想一下jQuery的Deferred(同样有

    resolve 和 then),这两个方法将会是近似的效果。then 会参考 pending 的状态,如果是等待状态则将回调保存(push),

    否则立即调用回调。resolve 则将肯定这个Promise,更新值的同时运行完所有保存的回调。例如:

    var oneLater = function(){
        var result = def();
        
        setTimeout( function(){
            result.resolve( 1 );
        },1000);     
        
        return result;
    };
     
    oneLater().then( function(value){
        console.log( value );    
    } );

    为了解决多次调用resolve, 可以给个状态判断,代码修改如下:

     var def = function(){
         var pending = [], value,
             
             self = {
                 resolve: function( _value ){
                     if( pending ){
                         value = _value;
                         for( var i = 0, len = pending.length; i < len; i++ ){
                             var callback = pending[ i ];
                             callback( value );
                         }
                         pending = undefined;
                     }
                 },
                 
                 then: function(callback){
                     if( pending ){
                         pending.push( callback );
                     }else{
                         callback( value );
                     }
                 }
             };
             
         return self;
     };

    分离der和promise

    在前面的实现中,def 生成的对象同时拥有 then 方法和 resolve 方法。按照定义,promise 只关心的是 then 方法,至于

    触发 promise 改变状态的 resolve,是另一回事。所以,Q 接下来将拥有 then 方法的 promise,和拥有 resolve 的def

    分离开来,各自独立使用。这样就好像划清了各自的职责,各自只留一定的权限,这会使代码逻辑更明晰,易于调整。

    看desige/q3.js:

    var isPromise = function( value ){
        return value && typeof value.then === "function";
    };
    
    var def = function(){
        var pending = [], value,
            
            self = {
                resolve: function( _value ){
                    if(( pending ){
                        value = _value;
                        for( var i = 0, len = pending.length; i < len; i++ ){
                            var callback = pending[ i ];
                            callback( value );
                        }
                        pending = undefined;
                    }
                },
                
                promise: {
                    then: function(){
                        if( pending ){
                            pending.push( callback );
                        }else{
                            callback( value );
                        }
                    }
                }
            };
            
            return self.promise;
    };

    实现promise级联

    接下来会是相当重要的一步。到前面的q3为止,所实现的 promise 都是不能级联的。但你所熟知的promise应该支持这样的语法:

    promise.then( step1 ).then( step2 );

     以上过程可以理解为,promise 将可以创造新的 promise,且取自旧的 promise 的值(前面代码中的 value )。要实现 then

    的级联,需要做到一些事情:

    1,then 方法必须返回 promise;

    2,这个返回的 promise 必须用传递给 then 方法的回调运行后的返回结果,来设置自己的值;

    3,传递给 then 方法的回调,必须返回一个 promise 或值;

    design/q4.js 中,为了实现这一点,新增了一个工具函数 ref:

    var ref = function( value ){
        if( value && typeof value.then === "function" ){
            return value;
        }
        
        return {
            then: function( callback ){
                var _value = callback( value );
                return ref( _value );
            }
        };
    };

    如果这个 ref 函数入参 value 是非 promise 对象的话,那么,就会把 value 传递给下一个回调函数,包装成 promise 对象。

    ref("step1").then(function(value){
        console.log(value); // "step1"
        return 15;
    }).then(function(value){
        console.log(value); // 15
    });

    你可以看到value是怎样传递的,promise 的级联需要做到的也是如此。

    var thenable = function( value ){
        if( value && typeof value.then === "function" ){
            return value;
        }
        
        return {
            then: function( callback ){
                var _value = callback( value );
                return thenable( _value );
            }
        };
    };
    
    var defer = function(){
        var pending = [], value,
            
            self = {
                resolve: function( _value ){
                    if( pending ){
                        //values wrapped in a promise
                        value = thenable( _value );
                        
                        for( i = 0, len = pending.length; i < len; i++ ){
                            var callback = pending[ i ];
                            value.then( callback ); // the called instead.
                        }
                        pending = undefined;
                    }
                },
                
                promise: {
                    then: function( _callback ){
                        var result = defer();
                        
                        var callback = function( value ){
                            var _value = _callback( value );
                            result.resolve( _value );
                        };
                        
                        if( pending ){
                            pending.push( callback );
                        }else{
                            value.then( callback );
                        }
                        
                        return result.promise;
                    }
                }
            };
            
            return self;
    };

    1,每次 then 都会生成新的 promise 对象;

    2, 传入的 _callback 都会包装成 callback 函数,新生成的 defer 对象里放置的 resolve 方法,这样是在旧的defer对象resolve时,

    通过defer的promise挂接着的callback函数,就可以让新的defer对象继续resolve下去;

    下面是测试代码:

    var deffered = defer();
    
    deffered.promise.then(function( value ){
            console.log( value );
            return 2;
    }).then(function( value ){
        console.log( value );
    });
    
    setTimeout( function(){
        deffered.resolve( 1 );
    }, 1000);

     加入错误处理

    promise的 then 方法应该可以包含两个参数,分别是肯定和否定状态的处理函数(onFulfilled 与 onRejected)。前面

    我们实现的 promise 还只能转变为肯定状态,所以,接下来应该加入否定状态部分。

    请注意,promise的 then 方法的两个参数,都是可选参数。design/q6.js 加入了工具函数 reject 来帮助实现 promise 

    的否定状态。

    var reject = function( reason ){
            return {
                then: function( callback, errback ){
                    return ref( errback( reason ) );
                }
            };
    };

    接下来,再看看design/q6.js的其余部分是:

    var defer = function(){
        var pending = [], value,
            
            self = {
                resolve: function( _value ){
                    if( pending ){
                        value = ref( _value );
                        for( var i = 0, len = pending.length; i < len; i++ ){
                            value.then.apply( value, pending[i] );
                        }
                        pending = undefined;
                    }
                },
                
                promise: {
                    then: function( _callback, _errback ){
                        var result = defer();
                        
                        _callback = _callback || functioin( value ){
                            return value;
                        };
                        
                        _errback = _errback || function( reason ){
                            return reject( reason );
                        };
                        
                        var callback = function( value ){
                            result.resolve( _callback( value ) );
                        };
                        
                        var errback = function( value ){
                            result.resolve( _callback( value ) );
                        };
                        
                        if( pending ){
                            pending.push([ callback, errback ]);
                        }else{
                            value.then( callback, errback );
                        }
                        
                        return result.promise;
                    }
                }
            };
            
            return self;
    };
  • 相关阅读:
    PHP+Ajax手机移动端发红包实例
    PHP+Ajax点击加载更多列表数据实例
    Thinkphp带表情的评论回复实例
    PHP+Mysql查询上一篇和下一篇文章实例
    PHP通过session判断防止表单重复提交实例
    PHP+Ajax微信手机端九宫格抽奖实例
    PHP+Ajax+plupload无刷新上传头像代码
    PHP原生开发的各大音乐平台API接口
    js 获取浏览器高度和宽度值(多浏览器)(转)
    NPOI、MyXls、Aspose.Cells 导入导出Excel(转)
  • 原文地址:https://www.cnblogs.com/branches/p/4905634.html
Copyright © 2020-2023  润新知