您的位置:首页 > Web前端 > JQuery

jQuery源码学习(版本1.11)-Deferred

2015-04-24 00:57 344 查看

概述

本分详细分析jquery-1.11.1.js源码文件行数:3251~3391;
代码简介:
使用jQuery.extend扩展了Deferred和when两个工具函数,其中when算是对Deferred的扩展应用;
Deferred函数返回一个Deferred对象,Deferred对象可分三种情况(resolve,reject,notify)分别保存各自的回调函数,这些回调会在Deferred状态变更的时候执行,不同的状态执行其各自的回调。可以说Deferred是管理异步回调的利器,可用于类似ajax请求,根据返回结果执行不同回调的场景;
When函数则是扩展Deferred,返回一个Deferred对象中的promise对象(只能添加回调,不能变更自身状态),这个对象可根据多个Deferred对象的状态去改变其状态;
下文对代码详细分析。

代码分析

// 扩展工具函数Deferred和when
jQuery.extend({

Deferred: function( func ) {
var tuples = [
// 定义嵌套数组,内层数组里的项对应action, add listener, listener list, final state,每一个数组代表Deferred对象的一种状态
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],
// 代表promise当前状态
state = "pending",
// 定义promise对象
promise = {
// 返回状态
state: function() {
return state;
},
// 往deferred添加回调,实际就是同时加进done跟fail的Callbacks对象里
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
// 一次性往deferred添加三种情况的回调
// 并且可返回新deferred对象里的promise对象(不是直接返回deferred对象,这点很重要)
// 返回的promise对象可以继续添加回调,但是只能根据原deferred的结果执行回调
// 相当于依赖,生成一个子对象,依赖父对象的结果执行回调,延长了原deferred的作用范围
then: function( /* fnDone, fnFail, fnProgress */ ) {
// 缓存参数,以免后续作用域变更导致变量名冲突
var fns = arguments;
// promise()返回的是promise而不是deferred
return jQuery.Deferred(function( newDefer ) {
// 使用each将fns全部加进newDefer,并不是直接添加,可以看出外面还套了一层funcion
jQuery.each( tuples, function( i, tuple ) {
var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
// fn外面套了一层function才加进[ done | fail | progress ]三种场景的list中
deferred[ tuple[1] ](function() {
// 执行fn,得到返回值
var returned = fn && fn.apply( this, arguments );
// 返回值存在promise方法,说明是promise对象或deferred对象
// 则将newDefer的触发方法[ done | fail | progress ] 加进去,returned又成为触发newDefer的另一个父对象,又必须触发returned才会触发newDefer
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
// 否则直接触发newDefer已添加的回调
} else {
newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
}
});
});
fns = null;
}).promise();
},
// 如果obj存在,使用jQuery.extend将promise扩展进去(即promise的属性会复制到obj中),否则直接返回promise对象
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
},
// 定义deferred对象
deferred = {};

// Keep pipe for back-compat
promise.pipe = promise.then;

// 调用each方法,实际就是用deferred,对tuples里的数组(三个Callbacks对象)进行包装的过程
jQuery.each( tuples, function( i, tuple ) {
// list获得的是一个Callbacks对象
var list = tuple[ 2 ],
stateString = tuple[ 3 ];

// 引用Callbacks对象的add方法包装起来,promise[ done | fail | progress ] = list.add
// promise对象里引用了[ done | fail | progress ] 三种方法,但没有[ resolve | reject | notify ]方法
// 表明promise无法触发回调
promise[ tuple[1] ] = list.add;

if ( stateString ) {
// 往list添加方法,由Callbacks对象的源码可知,这些方法会在list调用fire的时候执行
list.add(function() {
// 设置状态,只会出现两种情况state = [ resolved | rejected ]
state = stateString;

// resolved和rejected对对立的,异或取反添加对应Callbacks对象的disable方法,让其执行
// 同时添加tuples[ 2 ][ 2 ].lock,即notify的方法
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
}

// 包装方法deferred[ resolve | reject | notify ]
deferred[ tuple[0] ] = function() {
// 实际就是调用Callbacks的fireWith方法,这里传递了上下文,可知最终执行方法的上下文就是promise或this
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
return this;
};
// 包装Callbacks的fireWith方法
deferred[ tuple[0] + "With" ] = list.fireWith;
});

// 将promise对象扩展进deferred对象
promise.promise( deferred );

// 创建对象的时候如果传入了fun参数,执行之
if ( func ) {
func.call( deferred, deferred );
}

// 返回deferred
return deferred;
},

// Deferred helper 接收多个Deferred对象作为参数,返回一个新Deferred对象的promise,新对象的状态由所有参数共同控制
when: function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
resolveValues = slice.call( arguments ),
length = resolveValues.length,

// 获取传入的Deferred对象个数,如果只有一个,则取0
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,

// 最终返回deferred.promise(),如果不传参,为0或大于1,则新建一个Deferred对象,否则直接为第一个参数
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),

// 当参数Deferred对象状态变化时,更新deferred状态
updateFunc = function( i, contexts, values ) {
// 实际是返回一个function到参数Deferred对象的list中,这样该Deferred对象状态变化时就会触发回调更新deferred状态
return function( value ) {
// 保存上下文,实际就是每次调用时的其中一个参数Deferred对象
contexts[ i ] = this;
// 保存该参数Deferred对象执行回调时传的参数
values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
// 直接判断values是哪个数组对象可知当前执行的参数Deferred是哪个状态
// deferred.notifyWith每当一个参数Deferred执行一次notify时都会执行
if ( values === progressValues ) {
deferred.notifyWith( contexts, values );

// 只有当所有参数Deferred对象的状态都是resolved时才会调用deferred.resolveWith
// 此时contexts即为resolveContexts已经存下了所有的参数Deferred对象,作为deferred里回调的参数
} else if ( !(--remaining) ) {
deferred.resolveWith( contexts, values );
}
};
},

// 定义三个数组,用于保存参数Deferred对象执行回调时的参数和上下文,作为deferred里回调的参数
progressValues, progressContexts, resolveContexts;

// 判断length长度,对每一个参数Deferred对象都使用done,fail,progress添加上updateFunc的返回值(即上述分析的一个更新deferred状态回调函数)
// 这样每个参数Deferred对象状态变更时,都会更新deferred的状态,实现对deferred的控制
if ( length > 1 ) {
progressValues = new Array( length );
progressContexts = new Array( length );
resolveContexts = new Array( length );
for ( ; i < length; i++ ) {
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;
}
}
}

// remaining为0则在创建过程中直接就调用resolveWith,状态就会直接变为resolved
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}

// 最终只返回promise对象,这样就只能添加回调,而无法自己触发回调
return deferred.promise();
}
});
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: