jQuery.Callbacks of Callback Function Queue Usage Detailed

  • 2021-06-28 10:46:44
  • OfStack

This article provides an example of the use of jQuery.Callbacks() callback function queues.Share it for your reference, as follows:

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.

Next, we'll look at the four standard control flags.

1.1 once

The callbacks object created is only allowed once by fireWith () [Note: Method fire () is the appearance mode of fireWith ()].


var callbacks = $.Callbacks("once");
callbacks.add(function(){console.log("f1");});
callbacks.fire(); // output  "f1"
callbacks.fire(); // Nothing happens, disabled in source code  list.disable()

1.2 memory

When the add() method is called, if the callbacks queue satisfies fired at that time & & firing = false (true execution complete) & & memory (required to be specified in the constructor), then the callback function entered by add () executes immediately, and the parameters of the callback function called by add are stored in the memory variable.The memory variable stores the parameters [context, arguments] used in the last call to callbacks.fireWith(...).

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(); // output  "f1", The list of functions has now been executed !
    callbacks.add(function(){console.log("f2");});  //memory It works here, it doesn't fire , 1 All results : f2
    callbacks.fire(); // Retrigger 1 Secondary, Output  f1 f2 .  firingStart = 0
    // and once1 Start using 
    callbacks = $.Callbacks("once memory");
    callbacks.add(function(){console.log("f3");});
    callbacks.fire(); // output  "f3", The list of functions has now been executed !
    callbacks.add(function(){console.log("f4");});      // No, fire , 1 All results : f4
    callbacks.fire(); // Because it is "once" Nothing will be done here 
});

1.3 unique

Whether a function in a callback function list can be duplicated or not depends on the add() method, which avoids adding multiple identical callback functions to the callback function list.


var f1 = function(){console.log("f1");};
var callbacks = $.Callbacks();
callbacks.add(f1);
callbacks.add(f1);
callbacks.fire(); // output  f1 f1
// Pass-through parameters  "unique"
callbacks = $.Callbacks("unique");
callbacks.add(f1); // effective 
callbacks.add(f1); // Do not add 
callbacks.fire(); // output : f1

1.4 stopOnFalse

By default, when the fireWith () method is executed, all functions in the entire callback function list are executed sequentially, but if stopOnFalse is set, when a function returns false, the function behind it is no longer executed.Even if memory is set, the function added again will not execute, that is, once a function returns false, the memory function will be disabled.However, if "once" is not set, calling fire again can trigger the callbacks again.


var f1 = function(){console.log("f1"); return false}; // Be careful  return false;
var f2 = function(){console.log("f2");};
var callbacks = $.Callbacks();
callbacks.add(f1);
callbacks.add(f2);
callbacks.fire(); // output  f1 f2
callbacks = $.Callbacks("memory stopOnFalse");
callbacks.add(f1);
callbacks.add(f2);
callbacks.fire(); // Output Only  f1
callbacks.add(function(){console.log("f3");}); // No output, memory Has lost its role 
callbacks.fire(); // Retrigger, Output f1

2. memory callback queue


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 Source

Description: For ease of understanding, some source codes have been modified, some functions have been reduced ~~


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有两个功能,根据单1职责角度来说,应该增加1个 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其实是1个队列结构,这里用 stack 有些混淆
          } else {
     // 进入条件1: firing === false && fired === false        说明从来没有 fire()过
     // 进入条件2: firing === false && fired === true && stack = [] 说明至少调用过1次,而且当前允许多次调用,可以通过lock()锁定
            fire(args);
          }
        }
        return this;
      },
      fire: function() {
        self.fireWith(this, arguments);
        return this;
      },
      fired: function() {
        return !!fired;
      }
    };
  return self;
};

4. Fantasy

The core of the jQuery.Callbacks() method is the fire() method, which is encapsulated in a function and is not directly accessible, so states like memory, firing, and fired are immutable to the external context.

It is also important to note that if the this object is used in the callback function, the this can be used directly to access the public API of the self object.Of course, you can also specify a reference object for this yourself with fireWith ().

The core idea of jQuery.Callbacks() is the Pub/Sub mode, which establishes loose coupling and efficient communication between programs.

More readers interested in jQuery-related content can view this site's topics: jQuery Common Plug-ins and Usage Summary, Ajax Usage Summary in jquery, jQuery Table (table) Operation Skills Summary, jQuery Drag and Skills Summary, jQuery Extended Skills Summary, jQuery Common Classic Special Effects Summary, andjQuery Animation and Utility Summary and jquery Selector Usage Summary

I hope that the description in this paper will be helpful to everyone's jQuery program design.


Related articles: