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(); } });
相关文章推荐
- 【代码片-1】 jQuery源码学习(版本1.11)-事件处理-实例函数
- jQuery源码学习(版本1.11)-整体架构
- jQuery源码学习(版本1.11)-事件处理-工具函数jQuery.event
- jQuery源码学习(版本1.11)-事件处理-整体架构
- jQuery源码学习(版本1.11)-事件处理-jQuery事件对象
- jQuery源码学习(版本1.11)-Sizzle代码架构及整体匹配流程
- jQuery源码学习(版本1.11)-data
- jQuery源码学习(版本1.11)-queue
- jQuery源码学习(版本1.11)-Callbacks
- jQuery源码学习(版本1.11)-构造函数
- jquery的2.0.3版本源码系列(7):3043行-3183行,deferred延迟对象,对异步的统一管理
- JQuery源码学习(2.1.1)之 版本结构 ---- day1
- jQuery源码研究分析学习笔记-jQuery.deferred()(12)
- jquery1.11源码学习——揭秘jquery的extend方法
- jQuery源码学习(版本1.11)-创建jQuery对象
- jquery1.11源码学习——揭秘jquery内幕
- 通过jQuery源码学习javascript
- Jquery 源码学习
- jQuery源码学习
- 通过 ES6 Promise 和 jQuery Deferred 的异同学习 Promise 推荐