jQuery.Callbacks()回调函数队列用法详解

1、jQuery.Callbacks

The jQuery.Callbacks() function, introduced in version 1.7, returns a multi-purpose object that provides a powerful way to manage callback lists. It supports adding, removing, firing, and disabling callbacks.

The $.Callbacks() function is internally used to provide the base functionality behind the jQuery $.ajax() and $.Deferred() components. It can be used as a similar base to define functionality for new components.

接下来,我们分别看下四个标准的控制标志。

1.1 once

创建的 callbacks 对象只允许被 fireWith() 一次 [注意:方法fire() 是 fireWith() 的外观模式]。

var callbacks = $.Callbacks("once"); callbacks.add(function(){console.log("f1");}); callbacks.fire(); //输出 "f1" callbacks.fire(); //什么也不发生,在源码中已经禁用了 list.disable()

1.2 memory

在调用 add() 方法时,如果这时 callbacks队列 满足 fired && firing = false(真执行完毕) && memory(需要在构造函数指定),那么add() 进去的回调函数会立即执行,而这个 add 进去的回调函数调用时的参数存储在 memory 变量中。memory 变量用于存储最后一次调用 callbacks.fireWith(...) 时所使用的参数 [context, arguments]。

If the Callbacks object is created with the "memory" flag as its argument, additional functions may be added and fired after the callback list is locked.

$(function($){ 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。 firingStart = 0 //与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",这里将什么也不执行 });

1.3 unique

回调函数列表中的函数是否可以重复,该特性与 add() 方法有关,可以避免在回调函数列表中加入多个相同回调函数。

var f1 = function(){console.log("f1");}; var callbacks = $.Callbacks(); callbacks.add(f1); callbacks.add(f1); callbacks.fire(); //输出 f1 f1 //传递参数 "unique" callbacks = $.Callbacks("unique"); callbacks.add(f1); //有效 callbacks.add(f1); //添加不进去 callbacks.fire(); //输出: f1

1.4 stopOnFalse

默认情况下,当执行 fireWith() 方法时,整个回调函数列表中的所有函数都会顺序执行,但如果设置了stopOnFalse,那么当某个函数返回false时,后边的函数将不再执行。即使设置了memory,再次添加的函数也不会执行了,即一旦某个函数返回 false 的情况下,会禁用 memory 功能。但如果没设置”once”,再次调用fire可以重新触发该callbacks。

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

2. memory 回调队列

var i = 0; var inc = function (s){ i++; alert(i +"$" + s); }; var callbacks = $.Callbacks('memory'); callbacks.add(function iteral() { callbacks.add(inc); if (i <= 1) { callbacks.fire(i); } }); callbacks.fire(i); callbacks.add(inc); /* list = []; list = [it]; --->fire(0), i=0 1、list = [it, inc] 2、push(fire(0)) 3、i++ [inc(0)] (i=1) shift()--->fire(0), i=1 1、list = [it, inc, inc]; 2、push(fire(1)), 3、i++ [inc(0)] 4、i++ [inc(0)] (i=3) shift()--->fire(1),i=3 1、list = [it, inc, inc, inc]; 2、i++ [inc(1)] 3、i++ [inc(1)] 4、i++ [inc(1)] (i=6) --->add(inc), i=6, memory=[this,1] 1、i++ [inc(1)] (i=7) */

3、 jQuery.CallBacks 源码

说明:为了便于理解,修改了部分源码,减少了一些功能~~~

jQuery.Callbacks = function (options) { // string --> object 改进建议:将未配置的参数缺省为false,而不是undefined。便于程序阅读和控制. options = optionsCache[options] || createOptions(options); var firing, memory, //Last fire value [context, args] (for memory lists) fired, firingLength, firingIndex, firingStart, list = [], stack = options.once === true ? false : [], // Stack of fire calls for repeatable lists fire = function (data) { // data --> [context, args] memory = !!options.memory && data; // false OR [context, arguments] fired = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; // 这里 list 放在条件判断中是因为执行回调函数可能会改变 list 的状态,比如 this.disable()。 for ( ; list && firingIndex < firingLength; firingIndex++) { if (list[firingIndex].apply(data[0], data[1]) === false && options.stopOnFalse === true) { memory = false; // 禁止 memory 功能,这样调用 add() 增加新回调函数不会立即自动调用 break; } } firing = false; if (list) { if (stack) { //进入条件: fired && firing === false && stack, 实现递归调用 if (stack.length) { fire(stack.shift()); // [[context1, arguments1], [context2, arguments2]] } } else if (memory) { // 进入条件: fired && firing === false && stack === undefined && 有memory字段(memory变量只能通过fire()函数修改) // 这里的 list = [],主要是用于性能优化,以防该对象长时间不执行,占用系统内存 list = []; } else { // 进入条件: fired && firing === false && stack === undefined && 没有memory字段, 说明必要继续保留的必要 self.disable(); } } }, self = { add: function() { if (list) { //几乎所有API都应该绑定这个条件,因为我们需要处理队列 var originLength = list.length; jQuery.each(arguments, function( _, arg) { if (jQuery.type(arg) === "function") { // (!(options.unique && self.has(arg))) unique字段的作用 if (!options.unique || !self.has(arg)) { list.push(arg); } } }); if (firing === true) { // 进入条件: 说明正在执行回调函数队列中,而当前执行的这个回调函数激活了add()函数,及时维护循环边界 firingLength = list.length; } else if (memory) { // 进入条件: memory && fired && firing === false, 说明之前的 fire() 行为已经完全结束 firingStart = originLength; fire(memory); } } return this; }, remove: function() { if (list) { jQuery.each(arguments, function( _, arg) { var lastIndex; while ((lastIndex = jQuery.inArray(arg, list, lastIndex)) >= 0) { list.splice(lastIndex, 1); if (firing === true) { // 及时更新边界条件,实现智能处理 if (lastIndex <= firingLength) { firingLength--; } if (lastIndex <= firingIndex) { firingIndex--; } } } }); } return this; }, has: function (func) { //这个API有两个功能,根据单一职责角度来说,应该增加一个 isNotEmpty() 接口(非空) return func ? jQuery.inArray(func, list) > -1 : !!(list && list.length); }, empty: function() { list = []; return this; }, disable: function() { // 彻底禁用该对象, stack禁用, memory禁用 list = stack = memory = undefined; return this; }, disabled: function() { return !list; }, lock: function() { stack = undefined; // 如果memory没有存储调用状态,直接禁用这个对象(可能是从未调用就被锁定,或者没有memory字段) if (!memory) { self.disable(); } return this; }, locked: function() { return !stack; }, fireWith: function (context, args) { args = args || []; var data = [context, args]; if (list && (fired === false || stack) ) { if (firing) { // 进入条件: firing === true && stack 说明当前正在执行回调函数队列 stack.push(data); // stack其实是一个队列结构,这里用 stack 有些混淆 } else { // 进入条件一: firing === false && fired === false 说明从来没有 fire()过 // 进入条件二: firing === false && fired === true && stack = [] 说明至少调用过一次,而且当前允许多次调用,可以通过lock()锁定 fire(args); } } return this; }, fire: function() { self.fireWith(this, arguments); return this; }, fired: function() { return !!fired; } }; return self; };

4、胡思乱想

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wzsxsy.html