$.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. 让你的Ubuntu也能像Windows那样显示网速和CPU温度

    致力于Linux桌面操作系统的平民化,一直强迫自己完全在Ubuntu系统下进行日常的电脑使用,但是用长了时间的Windows,还是有些习惯改不过来,比如只要在下载或者看在线视频的时候就会不自觉关注网速 ...

  2. install Active Directory域控制器

    设置Active Directory域控制器 正如我们在网络与系统配置专题文章中所提到的那样,我们已将两部服务器设置为对应于内部域“intdomain.com”的Active Directory域控制 ...

  3. HTTPS、SSL与数字证书介绍

    在互联网安全通信方式上,目前用的最多的就是https配合ssl和数字证书来保证传输和认证安全了.本文追本溯源围绕这个模式谈一谈. 名词解释 HTTPS:在HTTP(超文本传输协议)基础上提出的一种安全 ...

  4. yii快速入门与参考

    Yii 权威指南 http://www.yiichina.com/guide/ http://www.yiiframework.com/doc/guide/1.1/zh_cn/ Ⅰ.基本概念一.入口文 ...

  5. [Yii][RBAC]Yii中应用RBAC完全指南

    开端筹办 Yii供给了强大的设备机制和很多现成的类库.在Yii中应用RBAC是很简单的,完全不须要再写RBAC代码.所以筹办工作就是,打开编辑器,跟我来. 设置参数.建树数据库 在设备数组中,增长以下 ...

  6. MongDB主从复制、复制集

    主从复制比较简单,指定master.slave即可,其中master可写可读.slave只能读不能写.向master插入数据时,mongodb会自动将数据复制到slave节点.这样做的好处是读写分离, ...

  7. CodeForces - 269C Flawed Flow

    http://codeforces.com/problemset/problem/269/C 题目大意: 给定一个边没有定向的无法增广的残量网络且1是源点,n是汇点,给定每条边中的流.  让你把所有边 ...

  8. mvc Html.BeginForm 生成 ?Length=

    今天再做mvc Html.BeginForm的方法的时候发现提交的时候都会带length= ,后面查看网上文档资料是后台model设置最小长度生成的 改成  @using (Html.BeginFor ...

  9. HttpWebRequest抓数据遇到的问题

    1.有些网站访问速度慢,而且这个网站的连接数(比如全球内衣,另外对于女生各种什么内衣不懂的也可以上去查看了解哈),因为没有即时的关闭,造成抓取页面数据的时候超时也严重. 解决:把相应的HttpWebR ...

  10. [AngularJS] Extract predicate methods into filters for ng-if and ng-show

    Leaking logic in controllers is not an option, filters are a way to refactor your code and are compa ...