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


Posted in Javascript onJune 14, 2016

本文实例讲述了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、胡思乱想

jQuery.Callbacks() 方法的核心是 fire() 方法,将该 fire() 方法被封装在函数中不可直接访问,因此像 memory、firing、fired 这些状态对于外部上下文来说是不可更改的。

还有需要注意的是,如果回调函数中使用了 this 对象,可以直接用这个 this 来访问self对象的公有API。当然,也可以用 fireWith() 自己指定 this 的引用对象。

jQuery.Callbacks()的核心思想是 Pub/Sub 模式,建立了程序间的松散耦合和高效通信。

希望本文所述对大家jQuery程序设计有所帮助。

Javascript 相关文章推荐
JQuery操作Select的Options的Bug(IE8兼容性视图模式)
Apr 21 Javascript
原生Js实现简易烟花爆炸效果的方法
Mar 20 Javascript
jQuery实现图片预加载效果
Nov 27 Javascript
Knockout自定义绑定创建方法
Dec 26 Javascript
jquery分页插件jquery.pagination.js使用方法解析
Apr 01 Javascript
JavaScript获取短信验证码(周期性)
Dec 29 Javascript
Bootstrap里的文件分别代表什么意思及其引用方法
May 01 Javascript
Bootstrap table使用方法记录
Aug 23 Javascript
解决vue 按钮多次点击重复提交数据问题
May 10 Javascript
js+html5实现手机九宫格密码解锁功能
Jul 30 Javascript
Vue递归实现树形菜单方法实例
Nov 06 Javascript
javascript实现函数柯里化与反柯里化过程解析
Oct 08 Javascript
基于gulp合并压缩Seajs模块的方式说明
Jun 14 #Javascript
JS去除空格和换行的正则表达式(推荐)
Jun 14 #Javascript
javascript用正则表达式过滤空格的实现代码
Jun 14 #Javascript
三种带箭头提示框总结实例
Jun 14 #Javascript
js判断输入字符串是否为空、空格、null的方法总结
Jun 14 #Javascript
简单实现的JQuery文本框水印插件
Jun 14 #Javascript
JS不用正则验证输入的字符串是否为空(包含空格)的实现代码
Jun 14 #Javascript
You might like
PHP中spl_autoload_register()和__autoload()区别分析
2014/05/10 PHP
[原创]PHP简单开启curl的方法(测试可行)
2016/01/11 PHP
php实现URL加密解密的方法
2016/11/17 PHP
Whatever:hover 无需javascript让IE支持丰富伪类
2010/06/29 Javascript
基于JQuery的一个简单的鼠标跟随提示效果
2010/09/23 Javascript
JQuery开发的数独游戏代码
2010/10/29 Javascript
jquery表格内容筛选实现思路及代码
2013/04/16 Javascript
Jquery实现自定义弹窗示例
2014/03/12 Javascript
Javascript变量的作用域和作用域链详解
2015/04/02 Javascript
ajax读取数据后使用jqchart显示图表的方法
2015/06/10 Javascript
简单谈谈JavaScript的同步与异步
2015/12/31 Javascript
javascript css红色经典选项卡效果实现代码
2016/05/17 Javascript
聊一聊JS中this的指向问题
2016/06/17 Javascript
JS实现pasteHTML兼容ie,firefox,chrome的方法
2016/06/22 Javascript
Query常用DIV操作获取和设置长度宽度的实现方法
2016/09/19 Javascript
jquery日历插件e-calendar升级版
2016/11/10 Javascript
nodejs判断文件、文件夹是否存在及删除的方法
2017/11/10 NodeJs
react实现复选框全选和反选组件效果
2020/08/25 Javascript
详解vue 组件注册
2020/11/20 Vue.js
在Python中使用模块的教程
2015/04/27 Python
使用Python制作自动推送微信消息提醒的备忘录功能
2018/09/06 Python
Python 装饰器@,对函数进行功能扩展操作示例【开闭原则】
2019/10/17 Python
Pytorch保存模型用于测试和用于继续训练的区别详解
2020/01/10 Python
Python爬虫之Selenium实现窗口截图
2020/12/04 Python
世界最大的海报和艺术印刷商店:AllPosters.com
2017/02/01 全球购物
Booking.com英国官网:全球酒店在线预订网站
2018/04/21 全球购物
一句话工作感言
2014/03/01 职场文书
《三个小伙伴》教学反思
2014/04/11 职场文书
车辆年检委托书范本
2014/10/14 职场文书
2014年学习委员工作总结
2014/11/14 职场文书
2014年煤矿工人工作总结
2014/12/08 职场文书
格林童话读书笔记
2015/06/30 职场文书
pygame面向对象的飞行小鸟实现(Flappy bird)
2021/04/01 Python
HTML5来实现本地文件读取和写入的实现方法
2021/05/25 HTML / CSS
go语言中http超时引发的事故解决
2021/06/02 Golang
python神经网络ResNet50模型
2022/05/06 Python