- 浏览: 10793 次
- 性别:
- 来自: 广州
最近访客 更多访客>>
文章分类
最新评论
-
leaow567:
写的好,1.8里面有少些变化
jQuery1.7系列三: jQuery延迟列表(Deferred)
声明:写博客,是对自身知识的一种总结,也是一种分享,但由于作者本人水平有限,难免会有一些地方说得不对,还请大家友善 指出,或致电:tianzhen.chen0509@gmail.com
关注:国内开源jQuery-UI组件库:Operamasks-UI
jQuery版本:v1.7.1
jQuery1.7系列三: jQuery延迟列表(Deferred)
一.函数列表
很多时候,我们想要执行一系列的函数,比如,当一个ajax请求后想要执行2个成功函数;当一个动画结束后想要执行2个函数,那么我们很容易想到用一个数组来承载这些待执行函数,如下所示:
var callbacks = []; callbacks.push(function(){}); callbacks.push(function(){});
callbacks这个函数列表除了装载函数外,没有任何多余的控制能力,在很多时候我们需要更加智能的函数列表。比如,它可以控制不可以添加重复的函数;可以控制何时开始触发这些函数;可以控制当某个函数返回了false时后边的函数将不再执行……
如果拥有了这样强大的函数列表,那么很多不可思议的功能将可以轻松的实现,而这一切都可以在jQuery中找到踪迹。
二.jQuery加强的函数回调列表(Callbacks)
jQuery1.7提供了一个加强的函数回调列表,它不仅仅用于存放函数,最重要的是它提供了更加复杂的控制,如何时才开始执行这些函数,这些函数是否可以重复。大体情况可以看下图:
jQuery.Callbacks便是它的实现,比如,看个最简单的代码:
<script> $(function($){ var callbacks = $.Callbacks(); callbacks.add(function(text){console.log("f1"+text);}); callbacks.add(function(text){console.log("f2"+text);}); //调用fire/fireWith才会执行这些函数,并且可以传递参数 callbacks.fire("test");//输出结果: f1test f2test }); </script>
接下来,我们分别看下四个标准的控制标志。
1. once
见名知义,只可以触发一次函数列表,也就是只有第一次调用fire/fireWith才会生效。
<script> $(function($){ //注意传递了参数 "once" var callbacks = $.Callbacks("once"); callbacks.add(function(){console.log("f1");}); callbacks.fire();//输出 "f1" callbacks.fire();//什么也不发生 callbacks.add(function(){console.log("f2");}); callbacks.fire();//还是什么也不发生 //默认为非"once" var callbacks = $.Callbacks(); callbacks.add(function(){console.log("f3");}); callbacks.fire();//输出f3 callbacks.fire();//输出f3 }); </script>
2. memory
单看名字我觉得很难知道是什么意思,它的作用是在add一个函数的时候,如果这时候函数列表已经全部执行完了,那么刚刚add进去的函数会立即执行。
<script> $(function($){ //注意传递了参数 "once" var callbacks = $.Callbacks("memory"); callbacks.add(function(){console.log("f1");}); callbacks.fire();//输出 "f1",这时函数列表已经执行完毕! callbacks.add(function(){console.log("f2");});//memory作用在这里,没有fire,一样有结果: f2 callbacks.fire();//再触发一次,输出 f1 f2 //与once一起使用 callbacks = $.Callbacks("once memory"); callbacks.add(function(){console.log("f3");}); callbacks.fire();//输出 "f3",这时函数列表已经执行完毕! callbacks.add(function(){console.log("f4");});//没有fire,一样有结果: f4 callbacks.fire();//由于为"once",这里将什么也不执行 }); </script>
3. unique
函数列表中的函数是否可以重复,这个也很简单。
<script> $(function($){ var f1 = function(){console.log("f1");}; var callbacks = $.Callbacks(); callbacks.add(f1); callbacks.add(f1); callbacks.add(f1); callbacks.fire();//输出 f1 f1 f1 //传递参数 "unique" callbacks = $.Callbacks("unique"); callbacks.add(f1); //有效 callbacks.add(f1); //添加不进去 callbacks.add(f1); //添加不进去 callbacks.fire();//输出: f1 }); </script>
4. stopOnFalse
默认情况下,当fire函数列表的时候,整个列表里边所有函数都会一一执行,但如果设置了stopOnFalse,那么当某个函数返回了false时,后 边的函数将不再执行。即使设置了memory,再次添加的函数也不会执行了。但如果没有设置”once”,再次调用fire还是可以再次重新触发的。
<script> $(function($){ var f1 = function(){console.log("f1"); return false};//注意 return false; var f2 = function(){console.log("f2");}; var callbacks = $.Callbacks(); callbacks.add(f1); callbacks.add(f2); callbacks.fire();//输出 f1 f2 callbacks = $.Callbacks("memory stopOnFalse"); callbacks.add(f1); callbacks.add(f2); callbacks.fire();//只输出 f1 callbacks.add(function(){console.log("f3");}); //不会输出,memory已经失去作用了 callbacks.fire();//重新触发,输出f1 }); </script>
当然,Callbacks还提供了类似remove,disable,lock等方法,可以查看文档,就不一一介绍了,最主要的还是上边的核心思想以及四种控制其行为的状态标志位。
三.延迟队列(Deferred)
Deferred是一个延迟队列,基于上边提到的Callbacks,其实Callbacks本身就已经拥有延迟功能了(还记得我们要自己fire才可以触发函数列表吧)。而Deferred内部包含了三个Callbacks,它们的定义分别如下:
var doneList = jQuery.Callbacks( "once memory" ), failList = jQuery.Callbacks( "once memory" ), progressList= jQuery.Callbacks( "memory" ),
可见,doneList和failList只可以触发一次,而progressList可以触发多次,而这个Deferred可以用来作啥?我们先来看段代码:
<script> $(function($){ //xhr你可以认为就是一个Deferred var xhr = $.ajax({ url : "test.html", //此success函数会添加到内部的doneList中去 success : function(){ console.log("success"); }, //此error函数会添加到内部的failList中去 error : function(){ console.log("error"); } }); xhr.success(function(){ console.log("another success"); //既然success添加到doneList中去,而它又具有"once memory"标识,所以这里添加进去的函数会马上执行 xhr.success(function(){ console.log("aftersuccess"); }); }).error(function(){ console.log("anothererror"); }); //如果请求成功,输出结果将是 "success" "anothersuccess" "after success" //如果请求失败,输出结果将是 "error""another error" }); </script>
其实,Deferred的设计刚好用 在ajax上,在进行ajax请求的时候,我们往往要添加成功和失败的回调处理,而且有时并不止一个,所以内部用了doneList和failList两 个Callbacks,至于progressList是个比较特殊的东西,它相当于一个预处理的功能,在doneList和failList触发之前触 发,而且必须由用户自己去触发它,所以一般我们不用在意它。
那么有了Deferred,ajax请求怎么调用那些回调就显而易见了,只要在ajax请求后根据状态判断是成功还是失败,然后选择触发doneList或者failList就行了,没什么其它稀奇的事。
最 后再讲一下Deferred的promise方法,它是个挺好的概念来的,为了更好的理解它,我们回到上边的ajax实例。我们知道,doneList和 failList应该由ajax内部来进行触发,而你拥有的功能仅仅是加入函数而已,毕竟只有ajax自己才知道请求成功还是失败了,那如果你拿到了上边 的xhr变量后偏偏要自己去 xhr.fire(),不就可以破坏ajax的回调处理了?
虽然我想作为一个正常的开发者没人会这样去做吧,但jQuery作者还是留了后招,上边虽然我注释到 xhr 基本可以认为是一个Deferred,但严格上说并不完全是,它是调用了Deferred.promise()返回的一个弱Deferred,此 Deferred弱的地方就在于它没有fire/fireWith这样的方法,所以jQuery作者想得还是蛮周到的吧,当你想用Deferred开发自 己的东西时,也许也会利用到这一个方法的。
总的说就是,Deferred.promise可以造出一个Deferred,但此Deferred只可以添加函数,不可以执行真正的触发。
四.最后的话
jQuery的Deferred是一个设计挺巧妙的东西,如果你想实现自己的异步的队列,完全可以参考它的设计,关于所要掌握的一些概念,在上边都已经提 到了,这只是一个初端,但对你进一步去了解它我觉得还是有好处的,特别是如果你想研究它的代码实现,有了这些知识,就不会觉得非常难以理解了。
五. 附录: Callbacks源码注释
/* * Create a callback list using the following parameters: * * flags: an optional list of space-separated flags that will change how * the callback list behaves * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible flags: * * once: will ensure the callback list can only be fired once (like a Deferred) * 表示函数列表只有第一次fire()/fireWith()起作用 * * memory: will keep track of previous values and will call any callback added * after the list has been fired right away with the latest "memorized" * values (like a Deferred) * 在调用add()添加函数时,如果函数列表已经触发过并执行完毕,只要设置了此值,新添加进去的函数将马上执行,否则不执行 * * unique: will ensure a callback can only be added once (no duplicate in the list) * 设置了此值表示函数列表中的函数不可以重复(两个不同引用指向同个函数意为重复) * * stopOnFalse: interrupt callings when a callback returns false * 设置了此值,一旦某个函数返回了false,其后的函数将不会执行,但如果flags.once=false,那么再次 * 调用fire还是可以再次触发的 * */ //flags的值比空白隔开 ,比 "once memory" "unique stopOnFalse" jQuery.Callbacks = function( flags ) { // Convert flags from String-formatted to Object-formatted // (we check in cache first) //最终flags将会是一个对象,比如{once:true , unique:true} flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; var // Actual callback list //存放函数的数组 list = [], // Stack of fire calls for repeatable lists //如果flags.once=true,那么当你调用fire时,而且这时上一轮的fire还没有执行完毕,这时候将会把 [context , args]入栈 //然后等上一次fire完结后,它会继续判断stack里边是否还有值,有的话会拿出[context , args]再次执行所有的函数列表 stack = [], // Last fire value (for non-forgettable lists) //默认值为false,表示此函数列表还没有触发过,一旦此函数列表触发过了,将有两种可能的情况: //如果flags.once=true,那么memory=true,以后在fire时,一发现memory=true,将什么也不做 //如果flags.memory=true,那么memory=[context , args] memory, // Flag to know if list is currently firing //是否正在触发中 firing, // First callback to fire (used internally by add and fireWith) //从函数列表哪里开始执行 firingStart, // End of the loop when firing //执行的函数列表长度,也就是执行函数的个数 firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // Add one or several callbacks to the list //真正执行添加函数的内部方法 add = function( args ) { var i, length, elem, type, actual; for ( i = 0, length = args.length; i < length; i++ ) { elem = args[ i ]; type = jQuery.type( elem ); //传递了数组同样可以处理 if ( type === "array" ) { // Inspect recursively add( elem ); } else if ( type === "function" ) { // Add if not in unique mode and callback is not in //处理flags.unique,如果flags.unique=true,还要判断一下是否函数已经存在了 if ( !flags.unique || !self.has( elem ) ) { list.push( elem ); } } } }, // Fire callbacks //真正执行触发函数列表的方法 fire = function( context, args ) { args = args || []; //默认情况下,flags.memory=false,这时memory=true,表示已经执行过了 //如果flags.memory=true,memory=[context , arsg] memory = !flags.memory || [ context, args ]; firing = true; //下边为什么要这么麻烦呢,用三个参数来控制. //在add的时候,如果flags.memory=true,并且函数列表执行过了,这时候只要执行新的函数就行了, //而在add中会设置firingStart为合适的值,这样自然就只执行最后添加的函数了 firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; for ( ; list && firingIndex < firingLength; firingIndex++ ) { //这是唯一处理flags.stopOnFalse的地方 if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { //只要flags.stopOnFalse=true,并且函数返回了false,那么memory强制设为true,这时候flags.memory将会失去作用 //因为flags.memory=true的作用就是 设置memory=[context , args] memory = true; // Mark as halted break; } } firing = false; //当触发执行完毕后,还要检查一下stack的情况,处理flags.once=false,并且调用了多次的fire() if ( list ) { if ( !flags.once ) { if ( stack && stack.length ) { memory = stack.shift(); //memory=[context , args]在这里将用到了 self.fireWith( memory[ 0 ], memory[ 1 ] ); } } else if ( memory === true ) { //既然已经触发过了,并且没有额外的控制,如没有设置flags.once=false,没有设置flags.memory=true,那么作废该 Callbacks吧 self.disable(); } else { list = []; } } }, // Actual Callbacks object //这就是调用 jQuery.Callbacks()返回的对象,用户眼中的Callbacks实例 self = { // Add a callback or a collection of callbacks to the list add: function() { //只要未曾 disable此Callbacks,此条件将永远为true if ( list ) { var length = list.length; add( arguments );//调用真正的函数添加处理 // Do we need to add the callbacks to the // current firing batch? //如果当前Callbacks正在触发,那么只要修改firingLength的值就可以了,这样刚添加进去的函数自然就会执行了 if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away, unless previous // firing was halted (stopOnFalse) //在添加的时候,只有一种情况会触发函数列表,那就是flags.memory=true,而且 //如果flags.stopOnFalse=true,并且有函数返回了false,那么即使 //flags.memory=true,这时候memory也会被强制设置为true,flags.memory完全失去了作用。 } else if ( memory && memory !== true ) { firingStart = length; fire( memory[ 0 ], memory[ 1 ] ); } } return this; }, // Remove a callback from the list //有添加,自然也可以删除函数 remove: function() { if ( list ) { var args = arguments, argIndex = 0, argLength = args.length; for ( ; argIndex < argLength ; argIndex++ ) { for ( var i = 0; i < list.length; i++ ) { //找到要删除的函数了,这时候要看Callbacks是否真在执行,若真在执行,要修改一些内部状态 if ( args[ argIndex ] === list[ i ] ) { // Handle firingIndex and firingLength if ( firing ) { if ( i <= firingLength ) { firingLength--; //firingIndex表示正在执行函数的索引,如果 //要删除的函数在这个索引前面,说明firingIndex必须减1,然后把前边那个函数删了 if ( i <= firingIndex ) { firingIndex--; } } } // Remove the element list.splice( i--, 1 ); // If we have some unicity property then // we only need to do this once //如果函数是唯一的,那么重新开始下一个函数的删除 if ( flags.unique ) { break; } } } } } return this; }, // Control if a given callback is in the list //一个给定函数是否已经在函数列表中了 has: function( fn ) { if ( list ) { var i = 0, length = list.length; for ( ; i < length; i++ ) { if ( fn === list[ i ] ) { return true; } } } return false; }, // Remove all callbacks from the list //纯粹的清空而已,不影响函数列表的添加和触发 empty: function() { list = []; return this; }, // Have the list do nothing anymore //一经disable,这个函数列表就完全不起作用了,相当于废了,可以休息了 disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? //判断函数列表是否已经disable了 disabled: function() { return !list;//list==undefined就说明该函数列表废了 }, // Lock the list in its current state //一旦上了锁,stack=undefined,那么即使flags.once=false,调用再多的fire也是无济于事,其实Callbacks也相当于废了 lock: function() { stack = undefined; if ( !memory || memory === true ) { self.disable(); } return this; }, // Is it locked? locked: function() { return !stack; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { if ( stack ) { if ( firing ) { if ( !flags.once ) { //如果当前Callbacks正在触发,你这时又调用了一次fire,这时候只要flags.once=false,那么就会把[context , args]入栈 //这样当上一次的触发完成时,将会检查stack,然后入栈自动重新执行函数列表 stack.push( [ context, args ] ); } } else if ( !( flags.once && memory ) ) { //把条件转换成 !flags.once || !memory感觉好懂一些 //如果flags.once=false,那么继续触发吧 //或者flags.once=true,但 memory=false,表示还没触发过,那么执行第一次的触发吧 fire( context, args ); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once //memory=false表示还没有触发,其它值均表示触发过了 fired: function() { return !!memory; } }; return self; };
相关推荐
在这个主题中,我们将深入探讨jQuery1.7的压缩版和未压缩版之间的区别,以及它们在实际开发中的应用。 首先,我们来看"jQuery1.7压缩版"。这个版本的文件通常以较小的文件大小提供,例如`jquery-1.7.min.js`。压缩...
1. **Deferred对象(Deferred Objects)**: 1.7版本引入了Deferred对象,用于更好地管理异步操作,如Ajax请求和动画,支持链式调用和回调函数。 2. **事件绑定优化(Event Bindings)**: jQuery 1.7改进了事件绑定...
1. **选择器**:jQuery 1.7提供了一系列强大的CSS选择器,如ID选择器(#id)、类选择器(.class)、属性选择器([attr=value])等,使得选取DOM元素变得简单。 2. **DOM操作**:jQuery提供了方便的DOM操作接口,如$...
在这个标题为“jquery1.7 API”的压缩包中,包含的是jQuery 1.7.1版本的两个主要文件:`jquery-1.7.1.min.js` 和 `jquery-1.7.1.js`,以及一个API文档`jquery1.7_20111204.chm`。 1. **`jquery-1.7.1.min.js` 和 `...
标题“jquery1.7 中文API.rar”表明这是一款针对jQuery 1.7版本的中文API参考文档,以RAR格式压缩,方便用户下载和保存。这个API文档对那些不熟悉英文或想要快速查找中文解释的开发者来说尤其有用。 描述中提到的...
这三份文档的组合为jQuery 1.7的使用者和希望深入理解AJAX的开发者提供了宝贵的资源。jQuery 1.7 API文档是编写高效jQuery代码的关键参考,而AJAX中文手册和详解则帮助开发者更好地理解和应用AJAX技术,提升网页的...
jQuery 1.7的几大亮点: 新的事件API:.on()和.off()。 委托事件性能改进。 在IE 6/7/8中的更好地支持HTML5。 切换动画更加直观。 异步模块定义(AMD):现在在jQuery中已支持AMD API。 jQuery.Deferred对象功能...
**jQuery 1.7中文帮助文档** jQuery是一个广泛使用的JavaScript库,它极大地简化了JavaScript的DOM操作、事件处理、动画制作以及Ajax交互。版本1.7是jQuery的一个重要里程碑,引入了许多新特性、优化和改进,同时...
jQuery 1.7.2是1.7系列的最后一个稳定版本,它引入了一些关键改进和修复。其中最重要的变化包括: 1. **事件委托优化**:jQuery 1.7引入了`.on()`和`.off()`方法,替代了之前的`.bind()`, `.live()`, `.delegate()`...
**jQuery 1.7中文帮助手册** jQuery是一个广泛使用的JavaScript库,它极大地简化了JavaScript的DOM操作、事件处理、动画制作以及Ajax交互。版本1.7是jQuery的一个重要里程碑,引入了许多新特性、优化和改进,同时...
在“各种的jQuery版本和1.7的文档”这个主题中,我们将深入探讨jQuery的不同版本及其特性,尤其是重点讲解jQuery 1.7版本的文档。 1. **jQuery的版本历史**: - jQuery 1.32:在这个版本中,jQuery优化了DOM操作,...
通过阅读《jQuery 1.7 手册》,开发者不仅可以掌握jQuery的基本用法,还能了解到高级特性,如Deferred对象、AJAX的Promise化、性能优化技巧等。手册中的实例代码和解释可以帮助开发者在实践中遇到问题时迅速找到解决...
本文将深入探讨jQuery 1.5、1.6及1.7这三个重要版本中的关键特性、改进与最佳实践,旨在帮助开发者更高效地利用这些版本来提升项目性能和用户体验。 一、jQuery 1.5:Promise对象与 Deferred对象的引入 1.5版本...
- **Deferred对象和Promise接口**: jQuery 1.7引入了 Deferred 和 Promise 概念,用于异步编程,使得处理多个异步操作变得更加优雅。 - **`.on()`事件绑定**: 代替了`.bind()`, `.live()`, `.delegate()`等事件绑定...
这个压缩包文件“jQ1.7-1.8”包含了 jQuery 的 1.7、1.8 和 1.8.2 三个版本,它们是 jQuery 发展历程中的重要里程碑,为开发者提供了强大的工具和功能。 **1. jQuery 版本差异** - **jQuery 1.7**:在这个版本中,...
三、jQuery 1.7的使用 1. DOM操作:使用jQuery选择器,如`$("#id")`或`$(".class")`,可以快速定位DOM元素。接着,可以使用`.html()`, `.text()`, `.attr()`等方法进行内容修改、属性设置等操作。 2. 事件处理:`....
本篇文章将围绕jQuery 1.4、1.5及1.7这三个版本,深入解析其关键知识点,帮助开发者更好地理解和运用。 一、jQuery基础 1. 选择器:jQuery的核心在于强大的选择器功能,如ID选择器($("#id"))、类选择器($("....
jQuery是世界上最流行的JavaScript库之一,它极大地简化了JavaScript的DOM操作、事件处理、动画制作以及Ajax交互。在本文中,我们将深入探讨jQuery的1.7.1至1.9.0这几个版本之间的关键变化和改进,帮助开发者了解...