先来思考一个问题:一个需要耗时很长的操作比如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.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,当其所有都完成时候触发回调。