Queue源码
一、queue模块的意义
处理动画事件的回调金字塔。
无queue模块:
$("#animate1").animate({heihgt:"760px"},function(){
$("#animate2").animate({width:"1280px"},function(){
console.log("end");
});
});
有queue模块:
var fns=[
function(){$("#animate1").animate({heihgt:"760px"},dequeue},
function(){$("#animate2").animate({width:"1280px"},dequeue},
function(){console.log("end")},
]
$(document).queue("animate",fns);
function dequeue(){
$(document).dequeue("animate")
};
dequeue();
二、实现思路
$.queue(ele,type,data)方法:构建Data对象存储queue函数队列data,以type为键附着于ele元素中。
$.dequeue(ele,type)方法:逐个取出queue函数队列并执行。
$._queueHooks(ele,type)方法:构建另一个Data对象hooks。queue函数队列执行完成清空Data对象,该函数存储在hooks.empty中。
queue执行函数传参为next和hooks,以启用下一个queue执行函数(另一种方法是调用dequeue方法逐次执行queue函数)、或者调用hooks中函数,比如$ele.delay调用hooks.stop执行延时等。
注:next函数使用同koa中间件、async,不同于jquery-deferred
$ele.queue(type,data)方法:注册、更新或获取queue函数队列,参数data须为数组形式的函数或纯函数。
$ele.dequeue(type)方法:逐次执行queue函数队列。
$ele.clearQueue(type)方法:清空queue函数队列。
$ele.delay(time,type)方法:$ele.queue方法注册queue函数队列时,经过延迟时间time后,调用next执行下一个queue函数。参数time可以是"show","fast",或毫秒数。
$ele.promise(type,obj)方法:返回deferred.promise对象,用以注册done回调函数,该回调函数在各元素的queue函数均执行完成后执行,通过将resolve启动器添加到hooks.empty中实现。
三、源码解读
define([
"./core",
"./data/var/dataPriv",// 私有对象Data,存储数据或函数
"./deferred",// 延迟对象Deferred
"./callbacks"// 回调对象Callbacks
],function(jQuery,dataPriv){
jQuery.extend({
// 创建、更新或获取Data对象,Data对象通过dataPriv构造函数附着在elem上
// 结合dequeue方法使用的时候需要保证data为数组形式的函数或函数形式
// data为queue队列函数回调,参数为next,hooks,dequeue方法中约定
queue:function(elem,type,data){
var queue;
if ( elem ){
type=(type || "fx")+"queue";
queue=dataPriv.get(elem,type);// 获取私有Data对象存储数据
if ( data ){
if ( !queue || jQuery.isArray(data) ){// 当data为数组格式时,创建、更新或获取私有Data对象
queue=dataPriv.access(elem,type,jQuery.makeArray(data));
}else{
queue.push(data);
}
}
return queue || [];
}
},
// 获取queue方法注册的Data对象,逐个执行queue队列函数,全部执行完成时调用hooks.empty.fire
// 下一个queue队列函数执行需要在queue方法注册的data中调用next函数参数
dequeue:function(elem,type){
type=type || "fx";
var queue=jQuery.queue(elem,type),// 获取queue方法添加的data数据,可能为函数
startLength=queue.length,
fn=queue.shift(),// 获取queue方法添加的data数据首项
hooks=jQuery._queueHooks(elem,type),
next=function(){
jQuery.dequeue(elem,type);
};
if ( fn==="inprogress" ){
fn=queue.shift();
startLength--;
}
if ( fn ){
// 数组头部添加"inprogress",重用代码考虑;以"fx"作为type值注册的queue队列函数不允许自动执行
if ( type==="fx" ){
queue.unshift("inprogress");
}
delete hooks.stop;// 为fn创建的hooks.stop腾出空间,但是不执行???
fn.call(elem,next,hooks);
}
// queue方法添加的data数据完毕后,执行_queueHooks注册的回调函数,清空queue模块创建的Data对象
if ( !startLength && hooks ){
hooks.empty.fire();
}
},
// 获取或注册回调函数Data对象,附着在elem上
// 获取后使用Callbacks对象的fire方法触发
_queueHooks:function(elem,type){
var key=type+"queueHooks";
return dataPriv.get(elem,key) || dataPriv.access(elem,key,{
empty:jQuery.Callbacks("once memory").add(function(){
dataPriv.remove(elem,[type+"queue",key]);
})
});
}
});
// 挂载在jquery对象上,便捷调用
jQuery.fn.extend({
// 无参数或仅有一个type参数时,获取以type值注册的Data对象数据
// 有type,没有data时,返回jquery元素对象
// 既有type又有data时,更新以type值注册的Data数据,并设置hooks回调,
// type类型为fx,调用$.dequeue执行data首项函数
queue:function(type,data){
var setter=2;
// 处理参数
if ( typeof type!=="string" ){
data=type;
type="fx";
setter--;
}
if ( arguments.length<setter ){// 无参数或仅有一个type参数时
return jQuery.queue(this[0],type);
}
return data===undefined ?
this :
this.each(function(){
var queue=jQuery.queue(this,type,data);
// 创建hooks存放回调函数,默认包含清空Data对象的empty回调函数,Callbacks的fire方法调用
jQuery._queueHooks(this,type);
// type类型为fx,执行queue函数队列首项函数
if ( type==="fx" && queue[0]!=="inprogress" ){
jQuery.dequeue(this,type);
}
});
},
// 调用执行queue方法注册的Data对象数据,即函数队列fn
dequeue:function(type){
return this.each(function(){
jQuery.dequeue(this,type);
});
},
// 清空queue方法注册的Data对象数据
clearQueue:function(type){
return this.queue(type || "fx",[]);
},
// 当前所有元素的queue队列函数执行完成后(dequeue方法触发执行),执行延迟对象的回调函数
promise:function(type,obj){
var tmp,
count=1,
defer=jQuery.Deferred(),
elements=this,
i=this.length,
// defer成功时回调启动器,添加到hooks.empty回调中,所有元素的queue函数都执行完成后触发
resolve=function(){
if ( !(--count) ){
defer.resolveWith(elements,[elements]);
}
};
if ( typeof type!=="string" ){
obj=type;
type=undefined;
}
type=type || "fx";
while (i--){
tmp=dataPriv.get(elements[i], type+"queueHooks");
if ( tmp && tmp.empty ){
count++;
tmp.empty.add(resolve);
}
}
resolve();
return defer.promise(obj);
}
});
return jQuery;
});define([
"../core",
"../queue",
"../effects" // Delay is optional because of this dependency
],function(jQuery){
// 通过setTimeout函数延迟执行下一个queue队列函数
jQuery.fn.delay=function(time,type){
time=jQuery.fx ? jQuery.fx.speeds[time] || time : time;// 延迟时间,slow、fast转化为毫秒数
type=type || "fx";
return this.queue(type,function(next,hooks){
var timeout=window.setTimeout(next,time);
hooks.stop=function(){
window.clearTimeout(timeout);
};
});
};
return jQuery.fn.delay;
});三、源码解读
function ff1() {
console.log(1)
}
function ff2() {
console.log(2)
}
function succ() {
console.log('done')
}
$body = $('body')
$body.queue('mx', ff1);
$body.queue('mx', ff2);
var promise = $body.promise('mx');
promise.done(succ);
setInterval(function() {
$body.dequeue('mx') // 先弹出1,2,最后是"done"
}, 1500)