$.Callbacks 是 jQuery 提供的可以方便地处理各种回调(callback)列表的类,其源代码是闭包的经典实现。

基本原理就是通过在闭包环境内保存一个 list = [] 数组用于存储回调列表,并用 firing,firingStart,firingLength,firingIndex等标志位来控制闭包的有序执行,下面是最重要的2个内部函数,触发函数 fire 和 添加函数 add。

         fire = function (data) {
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;
for (; list && firingIndex < firingLength; firingIndex++) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
memory = false; // To prevent further calls using add
break;
}
}
firing = false;
if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}
}

fire 函数

             add: function () {
if ( list ) {
// First, we save the current length
var start = list.length;
(function add( args ) {
jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg );
if ( type === "function" ) {
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
} else if ( arg && arg.length && type !== "string" ) {
// Inspect recursively
add( arg );
}
});
})( arguments );
// Do we need to add the callbacks to the
// current firing batch? if (firing) {
console.log('firing');
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
} else if ( memory ) {
firingStart = start;
fire( memory );
}
}
return this;
}

add 函数

引起我思考的是遍历回调列表时, firing 标志位的使用,遍历前赋值 firing = true,遍历完赋值 firing = false。在 add 函数内如果检查到 firing === true,则将回调函数加入到还没遍历完的列表末端即可。

可是问题来了,什么情况下会出现for 循环没执行完的情况下 add 函数被调用呢?即 add 函数执行时 firing 有没可能为 true?

我在 add 函数碰上 firing === true 时加上一句调试 console.log('firing')

1. 第一种情况,在某个回调函数内部再嵌套添加另一个回调。代码如下:

var callBacks = $.Callbacks();
callBacks.add(function () {
console.log('callBacks 0'); callBacks.add(function () {
console.log('callBacks 2');
});
}); callBacks.add(function () {
console.log('callBacks 1');
}); callBacks.fire();
//执行结果
//callBacks 0
//firing
//callBacks 1
//callBacks 2

还有没其他的可能呢?js 不是单线程的吗?是不是只要在每个回调函数内不再调用 callBacks.add 就不会碰上 firing === true 呢?

2. 利用浏览器对页面事件的响应处理

<input id="text1" type="text" />
<script type="text/javascript">
var text1 = document.getElementById('text1'); text1.onblur = function () {
console.log('.onblur() is called'); callBacks.add(function () {
console.log('callBacks 2');
});
}; text1.focus(); var callBacks = $.Callbacks(''); callBacks.add(function () {
console.log('callBacks 0');
text1.blur();
console.log('.blur() is called');
}); callBacks.add(function () {
console.log('callBacks 1');
}); callBacks.fire();
//IE下执行结果
//callBacks 0
//.blur() is called
//callBacks 1
//.blur() is called //非IE浏览器下执行结果
//callBacks 0
//.onblur() is called
//firing
//.blur() is called
//callBacks 1
//callBacks 2
</script>

这就是浏览器处理事件流程时带来的 js 执行流混乱。

在非 IE 下 .blur() 的调用会使当前执行堆栈挂起,转而执行 onblur 事件的回调函数,等 onblur处理完了才回头执行 .blur() 后面的代码。

IE则会等 .blur() 所在的上下文执行完之后才执行 onblur 事件的回调函数。

总结就是,浏览器无法保证 JavaScript 的单线程线性执行。详细可以看看这篇文章

Is javascript guaranteed to be single-threaded?

里面还讲到一个有趣的点,不要认为 alert 函数会挂起这个js 执行,window.onresize 事件在这种情况下还是可以被触发的,Linux 很容易, window 下可以通过改变分辨率的方式做到。

最后,我开始思考另一个问题,脱离了浏览器的 nodeJs 会出现这种问题吗?nodeJs能保证一个函数的执行不因为另一个函数而挂起吗?

jQuery 源码细读 -- $.Callbacks的更多相关文章

  1. JQuery源码解析--callbacks

    (function (global, factory) { factory(global); })(this, function (window, noGlobal) { var rootjQuery ...

  2. 读jQuery源码 - Deferred

    Deferred首次出现在jQuery 1.5中,在jQuery 1.8之后被改写,它的出现抹平了javascript中的大量回调产生的金字塔,提供了异步编程的能力,它主要服役于jQuery.ajax ...

  3. 读jQuery源码有感2

    那么就来读读jQuery源码的Callbacks部分. 一上来看原版源码 jQuery.Callbacks = function( options ) { // Convert options fro ...

  4. 读jQuery源码 - Callbacks

    代码的本质突出顺序.有序这一概念,尤其在javascript——毕竟javascript是单线程引擎. javascript拥有函数式编程的特性,而又因为javascript单线程引擎,我们的函数总是 ...

  5. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  6. jQuery源码 Ajax模块分析

    写在前面: 先讲讲ajax中的相关函数,然后结合函数功能来具体分析源代码. 相关函数: >>ajax全局事件处理程序 .ajaxStart(handler) 注册一个ajaxStart事件 ...

  7. jQuery源码笔记(一):jQuery的整体结构

    jQuery 是一个非常优秀的 JS 库,与 Prototype,YUI,Mootools 等众多的 Js 类库相比,它剑走偏锋,从 web 开发的实用角度出发,抛除了其它 Lib 中一些中看但不实用 ...

  8. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

  9. 读Zepto源码之Callbacks模块

    Callbacks 模块并不是必备的模块,其作用是管理回调函数,为 Defferred 模块提供支持,Defferred 模块又为 Ajax 模块的 promise 风格提供支持,接下来很快就会分析到 ...

随机推荐

  1. puppet任务计划

  2. 问题-[WIN764位系统]操作UDL驱动不全处理

    http://www-01.ibm.com/support/docview.wss?uid=swg21503768 方法# 1 单击“开始——运行” 输入一个命令类似如下:        C:\Win ...

  3. jmeter http get 登录

      登录页url:http://www.ablesky.com/login.do?fromurl=http%3A%2F%2Fwww.ablesky.com%2Findex.do 登录参数用firebu ...

  4. 如何屏蔽Button setClickable与setEnabled

    今天想让按键暂时无效,满足一定条件下才可以被用户按到.最初以为是setClickable,谁知搞错了.请看手册: 复制代码代码如下: public void setClickable (boolean ...

  5. [Oracle] - 性能优化工具(1) - AWR

    AWR快照 默认情况下,Oracle每隔一小时会自己主动产生一个快照,保存近期8天的快照. 我们能够通过例如以下语句获得产生快照的时间间隔和保存的天数: SYS@orcl(lx15)> sele ...

  6. 基于物联网技术和RFID电子客票的铁路自己主动检票机

    前言: RFID电子客票具有检阅速度快.信息量大.安全性高和高速物理定位的独特优势,随着标准的统一和成本的减少,它在铁路上有着巨大的应用前景,同一时候鉴于车站对物联网化的需求,我们设想出新一代的检票机 ...

  7. Java-struts2的问题 java.lang.NoClassDefFoundError: org/apache/commons/lang3/StringUtils

    缺commons-lang3-3.1.jar,添加之后就可以了

  8. Ajax-$.ajax()方法详解

    jquery中的ajax方法参数总是记不住,这里记录一下. 1.url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. 2.type: 要求为String类型的参数,请求方式(p ...

  9. careercup-数组和字符串1.2

    1.2 用C或C++实现void reverse(char *str)函数,即反转一个null结尾的字符串. C++实现代码: #include<iostream> #include< ...

  10. WPF加载Winform窗体时 报错:子控件不能为顶级窗体

    一.wpf项目中引用WindowsFormsIntegration和System.Windows.Forms 二.Form1.Designer.cs 的 partial class Form1 设置为 ...