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)