Deferred
最近在看deferred,有一些疑问,于是研究了一下,现在理解程度也就是70%-80%
这个方法基于callbacks,所以说,如果要看原理,最好先看一下callbacks的源码。下面我贴上源码,进行分析一下,会加上自己的理解,我主要困惑在when和then方法上,会重点说一下,这里说一下基本的源码分析网上有很多,大家可以搜一下,这里链接一个when和then方法的例子地址:
http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html
http://www.css88.com/jqapi-1.9/deferred.then/
ok,进入源码jQuery.extend({
Deferred: function( func ) {
//开始定义变量 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") ] ], state = "pending", promise = {
//返回状态 state: function() { return state; },
//无论成功与否都调用里面的参数(一般参数都是函数) always: function() { deferred.done( arguments ).fail( arguments ); return this; },
//分别对应完成后运行的函数,失败后运行的函数,正在运行过程中运行的函数 then: function( /* fnDone, fnFail, fnProgress */ ) {
//保存参数 var fns = arguments;
//直接返回一个deferred的方法,即直接返回一个deferred对象,
//这里的newDefer就是我们的return jQuery.Deferred(。。)的返回的新的deferred对象,源码Deferred方法的参数如果是函数的话
//会立即执行,源码如下:if ( func ) {func.call( deferred, deferred );},此时会把deferred作为参数传递进去
return jQuery.Deferred(function( newDefer ) { //循环遍历tuples,分别对不同状态注册函数 jQuery.each( tuples, function( i, tuple ) //取出“动作”:resolve(解决)、reject(拒绝)、notify(通知)
var action = tuple[ 0 ],
//取出对应回调函数,这里的i用的比较巧妙,如果fns[i]不存在返回false,否则返回函数 fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer
//这里dererred的各个状态的add方法添加了newDefer的fire方法,以后deferred调用fire的话,这个新的newDefer也同样fire
deferred[ tuple[1] ](function() {
//这里注意一下,这里相当于if(fn){returned = fn.apply(this,arguments)};
//这时候的returned相当于函数fn执行完毕之后的结果,后面举例子,就会看到效果了,这个this和arguments就是在fire的时候传递进来的,源码如下(for each中体现的):
//deferred[ tuple[0] ] = function() {deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );return this;}; var returned = fn && fn.apply( this, arguments );
//如果返回的结果是一个deferred对象,则把newDefer跟这个deferred对象关联,保证一个fire引起另外一个fire if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .done( newDefer.resolve ) .fail( newDefer.reject ) .progress( newDefer.notify ); } else {
//直接调用各个状态的fireWith方法,如果fn存在就直接把returned放入 newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } }); }); fns = null; }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object
//如果有参数就通过promise扩展obj,即把promise的属性都复制到到obj中,如果obj不存在则直接返回promise
//promise跟deferred区别在于deferred有很多可以改变对象的方法(rejectWith、resolveWith等),promise没有 promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; // Keep pipe for back-compat promise.pipe = promise.then; // Add list-specific methods
//这里主要就是把tuples的各个属性进行一一解析赋值到defferred中,
jQuery.each( tuples, function( i, tuple ) {
//list就是各个状态的callbacks的list,stateString是状态 var list = tuple[ 2 ], stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add
//把各个状态的done、fail、progress方法直接挂到callbacks的add上面
promise[ tuple[1] ] = list.add; // Handle state
//如果状态存在,也就是tuples的前两组
if ( stateString ) {
//done和fail状态,分别添加三个函数(改变自己的state,把另外一个状态设置为不可用,把第三个状态锁死),这样为了保证只有一个状态执行例如:done的时候fail和process就不能执行了 list.add(function() { // state = [ resolved | rejected ] state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock
//这个学一下,i^1,这个是异或运算符,效率高,特点:只有一个是1的时候才等于1
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } // deferred[ resolve | reject | notify ]
//定义fireWith方法,所谓的修改方法
deferred[ tuple[0] ] = function() { deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); return this; };
//把fireWith跟callbacks的fireWith进行绑定 deferred[ tuple[0] + "With" ] = list.fireWith; }); // Make the deferred a promise
//扩展deferred,把promise的属性扩展到defrred中
promise.promise( deferred ); // Call given func if any
//刚才上面提到的,Deferred()的参数如果是函数,则立即调用,参数就是将要返回的deferred对象
if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; }, // Deferred helper
//这个方法是jq的扩展,他的作用就是把放入其中的N个异步方法做一个计数,知道最后一个方法完成之后,才执行when后面的方法
when: function( subordinate /* , ..., subordinateN */ ) {
//定义变量,resolveValues把参数转化成数组 var i = 0, resolveValues = core_slice.call( arguments ), length = resolveValues.length, // the count of uncompleted subordinates
//remaining存放参数长度,也就是通过它来通知when是否所有的异步方法都执行完了
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
//如果remaining等于1,则肯定是deferred,如果参数大于1,则新定义一个deferred
deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values
//更新remaining长度,以及判断是否是最后一个异步,如果是最后一个,则调用deferred的fire方法,从而触发when()后面的方法调用
updateFunc = function( i, contexts, values ) { return function( value ) { contexts[ i ] = this; values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; if( values === progressValues ) { deferred.notifyWith( contexts, values ); } else if ( !( --remaining ) ) {
//计数为0的时候,调用deferred的fire方法 deferred.resolveWith( contexts, values ); } }; }, progressValues, progressContexts, resolveContexts; // add listeners to Deferred subordinates; treat others as resolved if ( length > 1 ) { progressValues = new Array( length ); progressContexts = new Array( length ); resolveContexts = new Array( length ); for ( ; i < length; i++ ) {
//判断传进来的参数是否是deferred,不是则直接计数减一,是的话,调用deferred的promise()方法返回promise对象,然后添加updateFunc函数,
//当参数中的函数fire的时候,就调用updateFunc函数,计数减一,直到为0
//整体思路,就是定义一个全局变量保存总共的参数个数,然后写一个公共函数,分配到各个参数函数中,每次函数执行完毕,就执行公共函数去操作公共变量减一,
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { resolveValues[ i ].promise() .done( updateFunc( i, resolveContexts, resolveValues ) ) .fail( deferred.reject ) .progress( updateFunc( i, progressContexts, progressValues ) ); } else { --remaining; } } } // if we're not waiting on anything, resolve the master if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } return deferred.promise(); } });
when函数例子:
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("出错啦!"); });
then函数例子:
var filterResolve = function() {
var defer = $.Deferred(),
filtered = defer.then(function( value ) {
return value * 2;
});
defer.resolve( 5 );
filtered.done(function( value ) {
alert(value);//10
});
};
filterResolve();
说说这个then的执行逻辑,在源代码中也提到过
1)定义一个deferred对象defer
2)defer的then方法执行过程,可以这么理解
a)var filtered = $.Deferred(),fn = function(value){return value*2;}
b)defer.add(filtered.resolve(fn()));
当调用defer的add的时候,相当于filtered的fire方法,而fire的参数是fn()函数的返回值,
当filtered再次done(add方法)的时候,直接执行上次的fire方法,而参数也是上一个的参数,参见callbacks的 once memory特性