jQuery的异步回调对象Deffered分析

  先来思考一个问题:一个需要耗时很长的操作比如setTimeout, ajax请求等,我们需要在延时操作后执行一个函数done。

  比如:

 var wait = function(){
    var tasks = function(){
      alert("执行完毕!");
    };
    setTimeout(tasks,5000);
  };

在wait执行完毕后,再执行done或者fail函数。

怎么做?当然,你可以在tasks函数中执行done() 或者 fail() ,但是不够优雅的实现。如果加入异步队列,添加类似jQuery的方法:$.when(wait()).done(function(){}).fail(function(){})。

怎么做到呢?原理很简单,如代码:

var dtd = $.Deferred(); // 新建一个deferred对象
  var wait = function(dtd){
    var tasks = function(){
      alert("执行完毕!");
      dtd.resolve(); // 改变deferred对象的执行状态
    };
    setTimeout(tasks,5000);
    return dtd;
  };
$.when(wait(dtd))
  .done(function(){ alert("哈哈,成功了!"); })
  .fail(function(){ alert("出错啦!"); });

添加一个变量dfd对象,执行完后,改变dfd的状态,状态改变,同时触发相应的方法。

具体来看看jQuery源码。

jQuery为我们抽象了3中状态:done, fail,progress;改变状态的方法:resolve,reject,notify;对应的状态码:resolved,reject;对应的回调的对象:jQuery.Callbacks("once memory")

来看其映射:

var tuples = [
                // action, add listener, listener list, final state
                [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
                [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
                [ "notify", "progress", jQuery.Callbacks("memory") ]
            ]

deferred对象要依赖于Callbacks对象实现。其解析移步Callbacks篇。

然后为deffered对象添加相应方法。

deferred = {};
// 添加[ resolve | reject | notify ]
deferred[ tuple[0] ] = function() {
    deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
    return this;
};
// 添加[ resolveWith | rejectWith | notifyWith ]
deferred[ tuple[0] + "With" ] = list.fireWith;

// 返回这个对象
return deferred;

//继承promise对象
promise: function( obj ) {
    return obj != null ? jQuery.extend( obj, promise ) : promise;
}

promise.promise( deferred );

为promise对象添加相应方法:

// 添加state, always, then, promise
promise = {
    state: function() {
        return state;
    },
    always: function() {
        deferred.done( arguments ).fail( arguments );
        return this;
    },
    then: function( /* fnDone, fnFail, fnProgress */ ) {
        var fns = arguments;
        return jQuery.Deferred(function( newDefer ) {
            jQuery.each( tuples, function( i, tuple ) {
                var action = tuple[ 0 ],
                    fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
                deferred[ tuple[1] ](function() {
                    var returned = fn && fn.apply( this, arguments );
                    if ( returned && jQuery.isFunction( returned.promise ) ) {
                        returned.promise()
                            .done( newDefer.resolve )
                            .fail( newDefer.reject )
                            .progress( newDefer.notify );
                    } else {
                        newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
                    }
                });
            });
            fns = null;
        }).promise();
    },
    promise: function( obj ) {
        return obj != null ? jQuery.extend( obj, promise ) : promise;
    }
},
// 添加pipe
promise.pipe = promise.then;

// 添加[ done | fail | progress ] = list.add
promise[ tuple[1] ] = list.add;

综上:

deferred的方法:

deferred.resolve() 手动改变deferred对象的运行状态为"已完成",从而立即触发done()方法。

deferred.reject() 这个方法与deferred.resolve()正好相反,调用后将deferred对象的运行状态变为"已失败",从而立即触发fail()方法。

deferred.notify()  手动改变deferred对象的运行状态为"正在进行"

deferred.state() 返回状态 

deferred.always()

deferred.then() 有时为了省事,可以把done()和fail()合在一起写,这就是then()方法。

deferred.promise() 没有参数时,返回一个新的deferred对象,该对象的运行状态无法被改变;接受参数时,作用为在参数对象上部署deferred接口。

deferred.done() 指定操作成功时的回调函数

deferred.fail() 指定操作失败时的回调函数

deferred.progress() 指定操作正在进行时的回调函数

deferred.pipe()

 $.when() 为多个操作指定回调函数。

 

promise的方法:

除了以上改变状态的三个方法(resolve, reject, notify)

done, fail, progress方法就是回调对象的add方法;

promise[ tuple[1] ] = list.add;

resolve, reject, notify方法就是回调对象的fire方法;

deferred[ tuple[0] ] = function() {
  deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
  return this;
};

deferred[ tuple[0] + "With" ] = list.fireWith;

其中then方法感觉比较复杂,其实是加了pipe方法的内容, 原来内容非常简单

pipe目前用的不多,意义不大,不详细研究

最后是when方法,其实是维护一个变量remaining,当其所有都完成时候触发回调。

 

 

 

 

  

  

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。