1 在开发的过程中,我们经常遇到某些耗时很长的javascript操作,并且伴随着大量的异步。
2 比如我们有一个ajax的操作,这个ajax从发出请求到接收响应需要5秒,在这5秒内我们可以运行其他代码段,当响应到达后,我们需要判断响应的结果(无非就是成功或者失败),并根据不同的结果 添加回调函数。
3 为了有效的简洁的添加回调函数jQuery引入了Callbacks。
4 而为了方便的 根据不同的结果(或者根据各种跟结果有关的逻辑,比如不管是成功或者失败) 添加回调函数,jQuery引入了Deferred。
$.ajax("test.html")
.done(function(){ alert("success"); })
.fail(function(){ alert("error"); });
5 因而Deferred与Callbacks是密不可分的,事实上,Callbacks也是从Deferred中分离出去的
回顾Callbacks
1 Callbacks大体架构
2 Callbacks源码分析:
define([ "./core", "./var/rnotwhite" ], function( jQuery, rnotwhite ) { // String to Object options format cache var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache /* 如果: var a = $.Callback('once memory') 则 optionsCache中会有这么一项:"once memory":{memory:true,once:true} */ function createOptions( options ) { var object = optionsCache[ options ] = {}; jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; }); return object; } /* * Create a callback list using the following parameters: * * options: an optional list of space-separated options that will change how * the callback list behaves or a more traditional option object * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options: * * once: will ensure the callback list can only be fired once (like a Deferred) * * memory: will keep track of previous values and will call any callback added * after the list has been fired right away with the latest "memorized" * values (like a Deferred) * * unique: will ensure a callback can only be added once (no duplicate in the list) * * stopOnFalse: interrupt callings when a callback returns false * */ jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options ); var // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired list是否已经被fire函数调用过 fired, // Flag to know if list is currently firing 当前是否正在调用fire函数 firing, // First callback to fire (used internally by add and fireWith) 第一个被执行的回调函数在list的位置 firingStart, // End of the loop when firing fire函数要运行的回调函数的个数 firingLength, // Index of currently firing callback (modified by remove if needed) 当前正在执行的回调函数的索引 firingIndex, //回调函数数组 list = [], // Stack of fire calls for repeatable lists 可重复的回调函数栈。我们可能会短时间内执行多次fire(),若当前的fire()正在迭代执行回调函数,而紧接着又执行了一次fire()时,会将下一次的fire()参数等保存至stack中,等待当前的fire()执行完成后,将stack中的fire()进行执行 stack = !options.once && [], // Fire callbacks fire = function( data ) { // data[0] 是一个对象,data[1]则是回调函数的参数 memory = options.memory && data; // 很精妙,仔细体会一下这句代码,如果调用Calbacks时传入了memory,则memory = data,否则memory = false fired = true; // 在调用本函数时,将fired状态进行修改 firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; // 迭代回调函数之前,将firing状态进行修改 for ( ; list && firingIndex < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { // 运行回调函数的同时,检测回调函数是否返回false,若返回false,且调用Callbacks时传入stopOnFalse参数,则终止迭代 memory = false; // To prevent further calls using add 既然终止迭代了,那么之后添加的回调函数都不应该被调用,将memory设置为false break; } } firing = false; // 迭代回调函数完成后,将firing状态进行修改 if ( list ) { if ( stack ) { // 没有使用once参数 if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) { // 使用了once memory参数,则在迭代完回调函数之后清空list list = []; } else { // 其他 self.disable(); } } }, // Actual Callbacks object self = { // 将一个新的回调函数添加至list 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 ); // 若参数中的元素为函数且(无unique参数或者list中没有该函数),则将该函数添加至list末尾 } } else if ( arg && arg.length && type !== "string" ) { // arg的长度不为0且每项的类型不为字符串,也就是args为这种情况:[[fun1,fun2...],[fun3,fun4]](不仅限于这种情况) // Inspect recursively add( arg ); } }); })( arguments ); // Do we need to add the callbacks to the // current firing batch? // 当Callback中的firingLength变为 动态的! 也就是:只要我们向list中添加了一个新的回调函数,即使在fire()运行过程中,改变也能立即体现出来 if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) { // 如果当前没有执行回调函数,且存在memory参数,则执行新添加的回调函数 firingStart = start; fire( memory ); } } return this; }, // Remove a callback from the list 将一个回调函数从list中移除 remove: function() { if ( list ) { jQuery.each( arguments, function( _, arg ) { var index; while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes if ( firing ) { if ( index <= firingLength ) { firingLength--; } if ( index <= firingIndex ) { firingIndex--; } } } }); } return this; }, // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); }, // Remove all callbacks from the list 清空数组 empty: function() { list = []; firingLength = 0; return this; }, // Have the list do nothing anymore 使用了这个方法,则意味着该回调对象失效了。 disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? disabled: function() { return !list; }, // Lock the list in its current state 给数组上锁 lock: function() { stack = undefined; if ( !memory ) { self.disable(); } return this; }, // Is it locked? locked: function() { return !stack; }, // Call all callbacks with the given context and arguments // 使用传入的context作为当前函数的执行上下文 fireWith: function( context, args ) { if ( list && ( !fired || stack ) ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if ( firing ) { stack.push( args ); // 如果当前正在迭代执行回调函数,则将新的fire参数推入stack中 } else { fire( args ); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once // 用来确定当前callback对象是否被fire()过 fired: function() { return !!fired; } }; return self; }; return jQuery; });