$.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. PHP中使用函数array_merge()合并数组

    如果明白数组其实就是map的话,我想你就会明白array_merge为什么要这么实现了 PHP中合并数组分成两种情况 1.如果这两个数组中有相同的字符串键名: <?php header('Con ...

  2. 微信开发第3章 通过accesstoken获取用户分组

    上一章我们获取到了access_token,那么我们可以试着拿token获取用户粉丝分组,调用接口地址为: http请求方式: GET(请使用https协议) https://api.weixin.q ...

  3. poj 1149 Pigs 网络流-最大流 建图的题目(明天更新)-已更新

    题目大意:是有M个猪圈,N个顾客,顾客要买猪,神奇的是顾客有一些猪圈的钥匙而主人MIRKO却没有钥匙,多么神奇?顾客可以在打开的猪圈购买任意数量的猪,只要猪圈里有足够数量的猪.而且当顾客打开猪圈后mi ...

  4. Java 线程第三版 第一章Thread导论、 第二章Thread的创建与管理读书笔记

    第一章 Thread导论 为何要用Thread ? 非堵塞I/O      I/O多路技术      轮询(polling)      信号 警告(Alarm)和定时器(Timer) 独立的任务(Ta ...

  5. 非堵塞socket实现android手机与PC的文件传输

    项目须要是通过WIFI建立手机和PC的通信,然后自己定义一个简单的协议对要传输的文件进行校验,传输的文件是2张3M的图片,要求考虑网络中断情况处理. 我这里採用的是非堵塞socket来实现的,之前查过 ...

  6. mybatis13 resultMap

    resultMap(入门) resultType :指定输出结果的类型(pojo.简单类型.hashmap..),将sql查询结果映射为java对象 . 使用resultType注意:sql查询的列名 ...

  7. Glossary of Terms in the JavaTM platform --reference

    http://docs.oracle.com/javase/tutorial/information/glossary.html field :A data member of a class. Un ...

  8. "ORA-00942: 表或视图不存在 "的原因和解决方法

    "ORA-00942: 表或视图不存在 "的原因和解决方法   采用Oracle数据库,使用Powerdesigner设计,生成Sql文件导入后查询出现“ORA-00942: 表或 ...

  9. Tcp抓包以及tcp状态解释

    tcp三次握手 发送端发送一个SYN=1,ACK=0标志的数据包给接收端,请求进行连接,这是第一次握手:接收端收到请求并且允许连接的话,就会发送一个SYN=1,ACK=1标志的数据包给发送端,告诉它, ...

  10. webrtc学习———记录二:canvas学习

    参考资料: http://bucephalus.org/text/CanvasHandbook/CanvasHandbook.html#getcontext2d https://developer.m ...