- 浏览: 520976 次
- 性别:
- 来自: 北京
博客专栏
-
jQuery技术内幕
浏览量:200705
最新评论
-
青春依旧:
学习html5哪里好?当然华清远见是首选!
[原创] jQuery源码分析-01总体架构 -
追梦1819:
[size=x-small][color=red][/colo ...
[原创] jQuery源码分析-04 选择器-Sizzle-设计思路 -
niuqiang2008:
学习学习
[原创] jQuery源码分析-04 选择器-Sizzle-工作原理 -
liuweihug:
jquery 解析正则表达式及常见的Regex规则和表达式 - ...
[原创] jQuery源码分析-02正则表达式-RegExp-常用正则表达式 -
liang8768:
mark!!!
[原创] jQuery源码分析-00前言开光
5. 异步队列 Deferred
5.1 概述
异步队列是一个链式对象,增强对回调函数的管理和调用,用于处理异步任务。
异步队列有三种状态:初始化(unresolved),成功(resolved),失败(rejected)。
执行哪些回调函数依赖于状态。
状态变为成功(resolved)或失败(rejected)后,将保持不变。
回调函数的绑定可以是同步,也可以是异步的,即可以在任何时候绑定。
(本节中的 绑定 注册 增加 具有相同的含义)
5.2 关键方法
先看看jQuery. Deferred()中的关键方法
分类 |
方法 |
说明 |
增加 |
deferred.done() |
增加成功回调函数 状态为成功(resolved)时立即调用 |
deferred.fail() |
增加失败回调函数 状态为失败(rejected)时立即调用 |
|
deferred.then() |
增加成功回调函数和失败回调函数到各自的队列中 便捷方法,两个参数可以是数组或null 状态为成功(resolved)时立即调用成功回调函数 状态为失败(rejected)时立即调用失败回调函数 |
|
|
deferred.always() |
增加回调函数,同时增加到成功队列和失败队列 状态已确定(无论成功或失败)时立即调用回调函数 |
执行 |
deferred.resolve() |
调用成功回调函数队列 通过调用deferred.resolveWith()实现 |
deferred.resolveWith() |
使用指定的上下文和参数执行成功回调函数 |
|
deferred.reject() |
调用失败回调函数队列 通过调用deferred.rejectWith()实现 |
|
deferred.rejectWith() |
使用指定的上下文和参数执行失败回调函数队列 |
|
其他 |
deferred.isRejected() |
判断状态是否为成功(resolved) |
deferred.isResolved() |
判断状态是否为失败(rejected) |
|
deferred.pipe()
|
每次调用回调函数之前先调用传入的成功过滤函数或失败过滤函数,并将过滤函数的返回值作为回调函数的参数 最终返回一个只读视图(调用promise实现) |
|
deferred.promise() |
返回deferred的只读视图 |
接下来将会jQuery._Deferred和jQuery.Deferred的源码详细剖析。
5.3 jQuery._Deferred
局部变量
// 参考资料: // 官网文档 http://api.jquery.com/category/deferred-object/ // Deferred机制 http://www.cnblogs.com/fjzhou/archive/2011/05/30/jquery-source-3.html // 在jQuery 1.5中使用deferred对象 http://developer.51cto.com/art/201103/248638.htm // 拿着放大镜看Promise http://www.cnblogs.com/sanshi/archive/2011/03/11/1981789.html // Promises/A http://wiki.commonjs.org/wiki/Promises/A
var // Promise methods // 注意,没有以下方法:resolveWith resolve rejectWith reject pipe when cancel // 即不允许调用resolve reject cancel等 promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ), // Static reference to slice // 静态引用slice方法,借鸡生蛋 sliceDeferred = [].slice; |
_Deferred:
_Deferred: function() { var // callbacks list // 回调函数数组(这里不翻译为队列,避免概念上的混淆) callbacks = [], // stored [ context , args ] // 存储上下文、参数,同时还可以标识是否执行完成(fired非空即表示已完成) // 这里的“完成”指回调函数数组中“已有”的函数都已执行完成; // 但是可以再次调用done添加回调函数,添加时fired会被重置为0 fired, // to avoid firing when already doing so // 如果已经触发正在执行,避免再次触发 firing, // flag to know if the deferred has been cancelled // 标识异步队列是否已被取消,取消后将忽略对done resolve resolveWith的调用 cancelled, // 异步队列定义(这才是正主,上边的局部变量通过闭包引用) // the deferred itself deferred = { // done( f1, f2, ...) // 增加成功回调函数,状态为成功(resolved)时立即调用 done: function() { // 如果已取消,则忽略本次调用 if ( !cancelled ) { // 将后边代码用到的局部变量定义在代码块开始处的好处: // 1.声明变量,增加代码可读性; // 2.共享变量,提高性能 // 注:多年写Java的经验,养成了全局变量在开头、临时变量随用随定义的习惯,看来JavaScript有些不同
var args = arguments, // 回调函数数组 i, // 遍历变量 length, // 回调函数数组长度 elem, // 单个回调函数 type, // elem类型 _fired; // 用于临时备份fired(fired中存储了上下文和参数)
// 如果已执行完成(即fired中保留了上下文和参数) // 则备份上下文和参数到_fired,同时将fired置为0 if ( fired ) { _fired = fired; fired = 0; } // 添加arguments中的函数到回调函数数组 for ( i = 0, length = args.length; i < length; i++ ) { elem = args[ i ]; type = jQuery.type( elem ); // 如果是数组,则递归调用 if ( type === "array" ) { // 强制指定上下文为deferred,个人认为这里没必要指定上下文,因为默认的上下文即为deferred deferred.done.apply( deferred, elem ); } else if ( type === "function" ) { callbacks.push( elem ); } } // 如果已执行(_fired表示Deferred的状态是确定的),则立即执行新添加的函数 // 使用之前指定的上下文context和参数args if ( _fired ) { deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); } } return this; },
// resolve with given context and args // 执行,使用指定的上下文和参数 resolveWith: function( context, args ) { // 满足以下全部条件,才会执行:没有取消 没有正在执行 没有执行完成 // 如果已取消 或 已执行完成 或 正在执行,则忽略本次调用 if ( !cancelled && !fired && !firing ) { // make sure args are available (#8421) // 确保args可用,一个避免null、undefined造成ReferenceError的常见技巧 args = args || []; // 执行过程中将firing改为1 firing = 1; try { // 遍历动态数组的技巧 while( callbacks[ 0 ] ) { // 注意这里使用指定的context,而不是this callbacks.shift().apply( context, args ); } } // JavaScript支持try/catch/finally finally { fired = [ context, args ]; firing = 0; } } return this; },
// resolve with this as context and given arguments // 把状态设置为Resolved // 设置的理解不准确,因为是否Resolved,是调用isResolved判断firing、fired的状态得到的。 // 可以理解为执行 resolve: function() { deferred.resolveWith( this, arguments ); return this; },
// Has this deferred been resolved? // 是否已执行(或解决)? // 在执行或已执行完毕,都认为已执行/解决 // “已”可能不准确,因为执行过程中也认为是已执行 isResolved: function() { // 正在运行中 // 或 // 已运行完(即fired不为空/0) return !!( firing || fired ); },
// Cancel // 取消异步队列 // 设置标记位,清空函数队列 cancel: function() { cancelled = 1; callbacks = []; return this; } };
return deferred; } |
5.4 jQuery.Deferred
// Full fledged deferred (two callbacks list) // 创建一个完整的异步队列(包含两个回调函数数组) // 异步队列有三种状态:初始化(unresolved),成功(resolved),失败(rejected)。 // 执行哪些回调函数依赖于状态。 // 状态变为成功(resolved)或失败(rejected)后,将保持不变。 Deferred: function( func ) { // _Deferred本无成功状态或失败状态,有四种状态:初始化、执行中、执行完毕、已取消 // 为了代码复用, 内部先实现了一个_Deferred // failDeferred通过闭包引用 var deferred = jQuery._Deferred(), failDeferred = jQuery._Deferred(), promise; // Add errorDeferred methods, then and promise jQuery.extend( deferred, { // 增加成功回调函数和失败回调函数到各自的队列中 // 便捷方法,两个参数可以是数组或null // 状态为成功(resolved)时立即调用成功回调函数 // 状态为失败(rejected)时立即调用失败回调函数 then: function( doneCallbacks, failCallbacks ) { // 上下文在这里有切换:虽然done返回的是deferred,但是fail指向failDeferred.done,执行fail是上下文变为failDeferred // 简单点说就是: // 调用done时向deferred添加回调函数doneCallbacks // 调用fail时向failDeferred添加回调函数failCallbacks
// 因此这行表达式执行完后,返回的是failDeferred deferred.done( doneCallbacks ).fail( failCallbacks ); // 强制返回deferred return this; }, // 注册一个callback函数,无论是resolved或者rejected都会被 调用。 // 其实,是把传入的函数(数组),同时添加到deferred和failDeferred // 并没有像我想象的那样,存到单独的函数数组中 always: function() { // done的上下文设置为deferred,fail的上下文设置为this // done和fail的上下文不一致吗?一致!在这里this等于deferred
// 但是这里如此设置上下文应该该如何解释呢?与then的实现有什么不一样呢?
// fail指向fail指向failDeferred.done,默认上下文是failDeferred,failDeferred的回调函数数组callbacks是通过闭包引用的, // 这里虽然将failDeferred.done方法的上下文设置为deferred,但是不影响failDeferred.done的执行, // 在failDeferred.done的最后将this替换为deferred,实现链式调用, // 即调用过程中没有丢失上下文this,可以继续链式调用其他的方法而不会导致this混乱
// 从语法上,always要达到的效果与then要达到的效果一致 // 因此,这行代码可以改写为两行(类似then的实现方式),效果是等价的: // deferred.done( arguments ).fail( arguments ); // returnr this; return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments ); }, // 增加失败回调函数 // 状态为失败(rejected)时立即调用 fail: failDeferred.done, // 使用指定的上下文和参数执行失败回调函数队列 // 通过调用failDeferred.rejectWith()实现 rejectWith: failDeferred.resolveWith, // 调用失败回调函数队列 // 通过调用failDeferred.resolve()实现 reject: failDeferred.resolve, // 判断状态是否为成功(resolved) isRejected: failDeferred.isResolved,
// 每次调用回调函数之前先调用传入的成功过滤函数或失败过滤函数,并将过滤函数的返回值作为回调函数的参数 // 最终返回一个只读视图(调用promise实现) // fnDone在状态是否为成功(resolved)时被调用 // fnFail在状态是否为失败(rejected)时被调用
// 关于其他的解释: // 1. 有的文章翻译为“管道机制”,从字面无法理解要表达什么含义,因此至少是不准确 // 2. 错误理解:所谓的pipe,只是把传入的fnDone和fnFail放到了成功队列和失败队列的数组头部 pipe: function( fnDone, fnFail ) { return jQuery.Deferred(function( newDefer ) { jQuery.each( { done: [ fnDone, "resolve" ], // done在后文中会指向deferred.done fail: [ fnFail, "reject" ] }, function( handler, data ) { var fn = data[ 0 ], action = data[ 1 ], returned; if ( jQuery.isFunction( fn ) ) { deferred[ handler ](function() { returned = fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise().then( newDefer.resolve, newDefer.reject ); } else { newDefer[ action ]( returned ); } }); } else { deferred[ handler ]( newDefer[ action ] ); } }); }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object
// 返回的是一个不完整的Deferred的接口,没有resolve和reject,即不能 修改Deferred对象的状态, // 这是为了不让外部函数提早触发回调函数,可以看作是一种只读视图。 // // 比如$.ajax在1.5版本后不再返回XMLHttpRequest,而是返回一个封装了 XMLHttpRequest和Deferred对象接口的object。 // 其中Deferred部分就是promise()得到 的,这样不让外部函数调用resolve和reject,防止在ajax完成前触发回调函数。 // 把这两个函数的调用权限保留给ajax内部。 promise: function( obj ) { if ( obj == null ) { // 实际只会执行一次promise,第一次执行的结果被存储在promise变量中 if ( promise ) { return promise; } promise = obj = {}; } var i = promiseMethods.length; // 又一种循环遍历方式 // 我习惯用: // for( i = 0; i < len; i++ ) 或 for( i = len-1; i >=0; i-- ) 或 for( i = len; i--; ) // jQuery真是遍地是宝! while( i-- ) { obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ]; } return obj; } }); // Make sure only one callback list will be used // 成功队列执行完成后,会执行失败带列的取消方法 // 失败队列执行完成后,会执行成功队列的取消方法 // 确保只有一个函数队列会被执行,即要么执行成功队列,要么执行失败队列; // 即状态只能是或成功、或失败,无交叉调用 // deferred和failDeferred的canceled属性,只能通过闭包引用,因此不用担心状态、上下文的混乱 deferred.done( failDeferred.cancel ).fail( deferred.cancel ); // Unexpose cancel // 隐藏cancel接口,即无法从外部取消成功函数队列 delete deferred.cancel; // Call given func if any // 执行传入的func函数 if ( func ) { func.call( deferred, deferred ); } return deferred; } |
5.5 jQuery.when
// Deferred helper // 异步队列工具函数 // firstParam:一个或多个Deferred对象或JavaScript普通对象 when: function( firstParam ) { var args = arguments, i = 0, length = args.length, count = length, // 如果arguments.length等于1,并且firstParam是Deferred,则deferred=firstParam // 否则创建一个新的Deferred对象(如果arguments.length等于0或大于1,则创建一个新的Deferred对象) // 通过jQuery.isFunction( firstParam.promise )简单的判断是否是Deferred对象 deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? firstParam : jQuery.Deferred(); // 构造成功(resolve)回调函数 function resolveFunc( i ) { return function( value ) { // 如果传入的参数大于一个,则将传入的参数转换为真正的数组(arguments没有slice方法,借鸡生蛋) args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; if ( !( --count ) ) { // Strange bug in FF4: // Values changed onto the arguments object sometimes end up as undefined values // outside the $.when method. Cloning the object into a fresh array solves the issue // 执行成功回调函数队列,上下文强制为传入的第一个Deferred对象 deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) ); } }; } // 如果参数多于一个 if ( length > 1 ) { for( ; i < length; i++ ) { // 简单的判断是否是Deferred对象,是则调用.promise().then(),否则忽略 if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) { // 增加成功回调函数和失败回调函数到各自的队列中 args[ i ].promise().then( resolveFunc(i), deferred.reject ); } else { // 计数器,表示发现不是Deferred对象,而是普通JavaScript对象 --count; } } // 计数器为0时,表示传入的参数都不是Deferred对象 // 执行成功回调函数队列,上下文强制为传入的第一个Deferred对象 if ( !count ) { deferred.resolveWith( deferred, args ); } // deferred !== firstParam,即deferred为新创建的Deferred对象 // 即length == 0 } else if ( deferred !== firstParam ) { // 执行成功回调函数队列,上下文强制为新创建的Deferred对象
deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); } // 返回传入的第一个Deferred或新创建的Deferred对象的只读视图 return deferred.promise(); } |
5.6 Deferred应用
l jQuery.ajax()
n TODO
5.7 可以学习的技巧
l 闭包
function a(){ var guid = 1; return function(){ return guid++; } }
var defer = a();
console.info( defer() ); // 1 console.info( defer() ); // 2 console.info( defer() ); // 3 console.info( defer() ); // 4 |
l 避免null、undefined造成ReferenceError的常见技巧
args = args || []; |
l 遍历动态数组的技巧
while( callbacks[ 0 ] ) { callbacks.shift().apply( context, args ); } |
l try/catch/finally 实现错误处理
语法 |
说明 |
|
try { // tryStatements } catch( exception ) { // catchStatements } finally { // finallyStatements } |
tryStatements |
必选项。 可能发生错误的语句。 |
exception |
必选项。任何变量名。 exception 的初始化值是扔出的错误的值。 |
|
catchStatements |
可选项。 处理在相关联的 tryStatement 中发生的错误的语句。 |
|
finallyStatements |
可选项。 在所有其他过程发生之后无条件执行的语句。 |
l 链式对象:通过返回this实现链式调用
方法 |
返回值 |
done |
this(即deferred) |
resolveWith |
this(即deferred) |
resolve |
this(即deferred) |
cancel |
this(即deferred) |
l 代码复用 $.each
jQuery.each( { done: [ fnDone, "resolve" ], // done在后文中会指向deferred.done fail: [ fnFail, "reject" ] }, function( handler, data ) { // 公共代码复用 }); |
5.8 后续
l Deferred在jQuery中的应用
l Deferred的自定义应用
评论
耐心的看吧,jQuery值得投入精力去研究
后边多的是,哈哈
发表评论
-
[原创] jQuery源码分析-04 选择器-Sizzle-设计思路
2011-11-14 20:59 6848作者:nuysoft/高云 QQ:47214707 Em ... -
[原创] jQuery源码分析-04 选择器-Sizzle-工作原理
2011-11-13 23:45 7881作者:nuysoft/高云 QQ:47214707 EM ... -
[原创] jQuery源码分析-02正则表达式-RegExp-常用正则表达式
2011-10-27 01:29 46633作者:nuysoft/JS攻城师/ ... -
[原创] jQuery源码分析-jQuery中的循环技巧
2011-10-27 00:36 14546作者:nuysoft/JS攻城师/高云 QQ:47214707 ... -
[原创] jQuery源码分析-10事件处理-Event-DOM-ready
2011-10-20 01:20 10598作者:nuysoft/JS攻城师/ ... -
[原创] jQuery源码分析-Java工程师应该向jQuery学习的8点建议
2011-10-18 23:56 12245作者:nuysoft/高云 QQ:47214707 EM ... -
[原创] jQuery源码分析-10事件处理-Event-事件绑定与删除-bind/unbind+live/die+delegat/undelegate
2011-10-18 22:31 15664作者:nuysoft/高云 QQ:47214707 EM ... -
[原创] jQuery源码分析-10事件处理-Event-源码结构
2011-10-17 01:01 12118作者:nuysoft/高云 QQ:47214707 EMail ... -
[原创] jQuery源码分析-07数据缓存-Cache
2011-10-13 19:55 12129作者:nuysoft/高云 QQ:47214707 EMail ... -
[原创] jQuery源码分析-06浏览器测试-Support
2011-10-13 19:19 9898作者:nuysoft/高云 QQ:47214707 EMail ... -
[原创] jQuery源码分析系列目录(持续更新)
2011-10-12 12:30 29350作者:nuysoft/高云 QQ:47214707 E ... -
[原创] jQuery源码分析-10事件处理-Event-概述和基础知识
2011-10-12 00:16 12007作者:nuysoft/高云 Q ... -
[原创] jQuery源码分析-08队列 Queue
2011-10-10 23:48 10788作者:nuysoft/高云 Q ... -
[原创] jQuery源码分析-03构造jQuery对象-工具函数
2011-09-29 23:21 29778作者:nuysoft/高云 QQ:47214707 E ... -
[原创] jQuery源码分析-15AJAX-类型转换器
2011-09-29 02:25 7404作者:nuysoft/高云 QQ:47214707 EM ... -
[原创] jQuery源码分析-03构造jQuery对象-源码结构和核心函数
2011-09-28 02:20 52961作者:nuysoft/高云 QQ:47214707 EMail ... -
[原创] jQuery源码分析-如何做jQuery源码分析
2011-09-27 00:22 14995近期在ITEYE陆续写了几篇jQuery源码分析,乐在其 ... -
[原创] jQuery源码分析-17尺寸和大小 Dimensions & Offset
2011-09-25 22:05 7047边读边写,不正确的地方,还请各位告诉我,多多交流共同学习, ... -
[原创] jQuery源码分析-15AJAX-前置过滤器和请求分发器
2011-09-23 00:09 13275边读边写,不正确的 ... -
[原创] jQuery源码分析-00前言开光
2011-09-21 23:42 46999jQuery源码分析 - 前言 jQuery凭借简洁的语法和 ...
相关推荐
- **异步队列Deferred**:探讨jQuery中用于处理异步操作的`Deferred`对象,解释其工作原理和如何实现链式调用。 - **队列Queue**:讲解队列在jQuery动画和AJAX请求中的作用,以及如何管理和控制队列中的任务执行...
jQuery源码分析 00 前言开光 01 总体架构 03 构造jQuery对象-源码结构和核心函数 ...05 异步队列 Deferred 08 队列 Queue 09 属性操作 10 事件处理-Event-概述和基础知识 15 AJAX-前置过滤器和请求分发器
### jQuery之Deferred源码剖析知识点 #### 一、Deferred和Promise简介 jQuery的Deferred对象是jQuery在ES6的Promise概念出现之前的一种实现,它允许开发者将异步操作的结果通过链式调用的方式处理,避免了传统的...
#### 五、异步队列Deferred 1. **概念**:`Deferred`对象是jQuery提供的一个用于处理异步操作的工具,可以解决异步操作中的多个回调问题。 2. **核心方法**: - `resolve()`:表示异步操作成功完成。 - `reject...
- 异步队列(Deferred):jQuery 1.7引入了 Deferred 对象,用于更好地处理异步操作,如Ajax请求。 - .on() 和 .off() 方法:取代了早期的 `.bind()`, `.unbind()`, `.delegate()`, `.undelegate()`,提供更灵活的...
本主题将深入探讨jQuery中的AJAX扩展,特别是关于自动放弃(Abandonment)和队列(Queue)的实现,这些特性对于优化异步请求管理和资源利用率至关重要。 **一、jQuery AJAX 基础** 在jQuery中,`$.ajax()`函数是...
4. 异步队列Deferred:这部分介绍jQuery中的异步编程机制,包括jQuery.Deferred对象的使用和它在处理AJAX请求、事件绑定等异步操作中的作用。 5. 队列Queue:用于管理事件处理和动画队列。这展示了jQuery如何使用...
3. 异步队列(Deferred):管理异步操作,允许注册回调函数以便在操作完成时触发。 4. 浏览器测试(Support):用于检测浏览器的兼容性,提供一系列检查方法,确保代码能够在不同的环境中正确运行。 5. 数据缓存...
Deferred是jQuery中用于简化异步编程的工具,它通过Callbacks来维护回调函数队列,从而使得复杂的异步流程能以更加直观的方式编写。Queue则是jQuery中处理同步队列的系统,通过Callbacks来驱动动画效果,使得动画的...
Deferred把回调函数注册到一个队列中,统一管理,并且可以同步或者异步地调用这些函数。jQuery.Deferred()用来构造一个Deferred对象。该对象有状态值,共有三种: Rejected, Resolved和初始状态。其中Resolved表示该...
#### 五、异步队列Deferred 1. **简介**:`Deferred`对象是jQuery中处理异步操作的关键,它可以跟踪异步操作的状态(pending、resolved或rejected),并且可以在状态改变时执行相应的回调函数。 2. **使用场景**:`...
jQuery中的Deferred对象能够以链式调用的方式组织回调函数,使得多个异步操作可以形成一个有序的队列。 Deferred对象的另一个重要特性是支持promise机制。Promise是一种设计模式,它允许你为异步操作的成功或失败...
接着详细分析了底层支持模块的源码实现,包括:选择器sizzle、异步队列deferred、数据缓存data、队列queue、浏览器功能测试support;最后详细分析了功能模块的源码实现,包括:属性操作attributes、事件系统events、...
资源名称:jQuery技术内幕:深入解析jQuery架构设计与...接着详细分析了底层支持模块的源码实现,包括:选择器 Sizzle、异步队列 Deferred、数据缓存 Data、资源太大,传百度网盘了,链接在附件中,有需要的同学自取。
- **$.Callbacks()**:创建可配置的回调函数队列,增强了异步处理。 - **$.Deferred()**:改进了Promise对象,支持链式操作,便于异步编程。 - **$.parseJSON()**:安全地解析JSON字符串,防止跨站脚本攻击。 **4. ...
接着详细分析了底层支持模块的源码实现,包括:选择器sizzle、异步队列deferred、数据缓存data、队列queue、浏览器功能测试support;最后详细分析了功能模块的源码实现,包括:属性操作attributes、事件系统events、...