1.定义
回调这个词对每个js使用者是如此的熟悉不过. 从字面看很容易理解,Callback就是对调,Callbacks就是多个回掉.在JQuery中就是回调队列, 也就是Callbacks中保存着很多个回调函数队列, 也就是按照顺序执行(按照加入队列的顺序触发这些函数,并不意味着第一个运行结束才调用第二个回调,因为js中存在异步)队列中的每一个回调函数. 我们也可以这么理解,也就是我们常说的事件订阅和发布模式. 说道事件,我们就容易理解了.每一次trigger就对调用一个回调函数.
那么Callbacks有什么用呢?在JQuery中,Deffered中使用Callbacks实现了Promise/A+标准. 解决异步的同步问题,解决代码深层嵌套.
2.Callbacks中的主要方法
callbacks.add() 回调列表中添加一个回调或回调的集合。
callbacks.disable() 禁用回调列表中的回调
callbacks.disabled() 确定回调列表是否已被禁用。
callbacks.empty() 从列表中删除所有的回调.
callbacks.fire() 用给定的参数调用所有的回调
callbacks.fired() 访问给定的上下文和参数列表中的所有回调。
callbacks.fireWith() 访问给定的上下文和参数列表中的所有回调。
callbacks.has() 确定列表中是否提供一个回调
callbacks.lock() 锁定当前状态的回调列表。
callbacks.locked() 确定回调列表是否已被锁定。
callbacks.remove() 从回调列表中的删除一个回调或回调集合。
3.主要参数
options = {
once: 回调对象仅触发(fire)一次
memory: 若true,在Callbacks正则触发事件过程中,新加入回调函数,新加入的函数将在旧的回调函数队列执行完成之后,促发新加入的.
unique: 在add操作中,相同的函数仅只一次被添加(push)到回调列表中
stopOnFalse:当回调函数返回false,中断列表中的回调循环调用,且memory === false,阻止在add操作中将要触发的回调
}
4.源码
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 // Flag to know if list is currently firing
firing,//是否正在触发
// Last fire value (for non-forgettable lists)
memory,//
// Flag to know if list was already fired
fired,//是否已经触发过
// End of the loop when firing
firingLength,//回调队列的长度
// Index of currently firing callback (modified by remove if needed)
firingIndex,//标记当前触发队列的标号(也就是记录当前触发到第几个)
// First callback to fire (used internally by add and fireWith)
firingStart,//回调队列的起始标号
// Actual callback list
list = [],//回调队列, 保存回调函数
// Stack of fire calls for repeatable lists
//触发队列, 多次触发的队列才把触发的上下文入栈
//(同一个时间只有一次触发队列执行,也就是说连续触发fire两次队列,第一次执行完,才会执行第二次[第一次触发,第二次触发...])
stack = !options.once && [],
// Fire callbacks
//触发回调函数, self使用函数fire实现最终的回调函数的调用
fire = function( data ) {
//如果是memory类型管理器, 记住fire的事件data,以便下次add的时候可以重新fire这个事件
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();
}
}
},
// Actual Callbacks object
//var cb = $.Callbacks();返回的实际是self对象, 在JQuery中很多这样的使用.使用闭包实现开闭和封装.
//有点类似Java中的内部类,例如使用内部类Iterator 实现对容器的遍历.通过类提供的接口,限制访问实际外部类的东西.
//self作为Callbacks的对外接口,我们使用它来访问Callbacks
self = {
// Add a callback or a collection of callbacks to the list
add: function() {
if ( list ) {
// First, we save the current length
//记录本次触发的队列长度位置,当是memory的时候可以接着触发新加入的回调函数
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 ) {
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;
},
// Remove a callback from the 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
fireWith: function( context, args ) {
if ( list && ( !fired || stack ) ) {
args = args || [];
//栈中存的上下文格式是[上下文(默认self为上下文), [参数1, 参数2...]]
args = [ context, args.slice ? args.slice() : args ];
//触发回调队列的时候,如果上一次触发还正在进行,那么久把当前触发的上下文和参数保存到栈stack中,
//这样, 上一次触发回调完成之后,可以从栈中取出上下文和参数,接着触发下一次
if ( firing ) {
stack.push( args );
} 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
fired: function() {
return !!fired;
}
};
//返回self
return self;
};