$.queue是jquery里面的一种队列,在动画里面用的比较多,也可以单独使用,今天看看这个是怎么玩的。

    $.queue

    这是一个挂在$全局方法上的一个添加队列的方式,例如:

    1. var obj={'name':'nb'},
    2.     f1=function(){console.log('a');}
    3. $.queue(obj,'a',f1);

    注意,上面的代码内部采用的是$._data在obj对象上挂载了名为aqueue的队列('a'+'queue'),和aqueueHooks的$.Callback('a'+'queueHooks')。

    $.Callback中装的是等队列完成后的回调方法,这个可以add多个的,具体的$.Callback使用,可以看 $.Callback使用

    $.dequeue

    这个方法是对应$.queue的触发方法,我们接着上面的demo,看看如何触发自定义的队列:

    1. var obj={'name':'nb'},
    2. f1=function(next,hook){
    3.         console.log('dequeue f1');
    4.         hook.empty.add(function(){
    5.             console.log('f1 end');
    6.         });
    7. },
    8. f2=function(next,hook){
    9.         console.log('dequeue f2');
    10.         hook.empty.add(function(){
    11.             console.log('f2 end');
    12.         });
    13. }
    14. $.queue(obj,'a',f1);
    15. $.queue(obj,'a',f2);
    16. $.dequeue(obj,'a');
    17. $.dequeue(obj,'a');
    18. $.dequeue(obj,'a');
    19. /*
    20. dequeue f1
    21. dequeue f2
    22. f1 end
    23. f2 end
    24. */

    我们可以看到,我们在obj上建立了一个名为'aqueue'的队列,然后在队列里面放入了两个方法,同时,每个方法在调用的时候,在'aqueueHooks'里面都添加了一个回调,如下图:

    注意,aqueue是一个队列,遵从先入先出的规则;aqueueHooks是一个$.Callback,会按照顺序执行add进入的方法。在队列里面是先把队列所有执行完才会调用到$.Callback里面的内容。

    每执行一个dequeue,其实就相当于从队列中出列一个。我们看上面的demo会发现,队列里面只有两个元素,为什么dequeue三次?因为最后一次是触发$.Callback的,我们先看以下$.dequeue的源码:

    1. dequeue: function( elem, type ) {
    2.     type = type || "fx";
    3.     var queue = jQuery.queue( elem, type ),
    4.         startLength = queue.length,
    5.         fn = queue.shift(),
    6.         hooks = jQuery._queueHooks( elem, type ),
    7.         next = function() {
    8.             jQuery.dequeue( elem, type );
    9.         };
    10.         // If the fx queue is dequeued, always remove the progress sentinel
    11.         if ( fn === "inprogress" ) {
    12.             fn = queue.shift();
    13.             startLength--;
    14.         }
    15.     if ( fn ) {
    16.         // Add a progress sentinel to prevent the fx queue from being
    17.         // automatically dequeued
    18.         if ( type === "fx" ) {
    19.             queue.unshift( "inprogress" );
    20.         }
    21.         // Clear up the last queue stop function
    22.         delete hooks.stop;
    23.         fn.call( elem, next, hooks );
    24.     }
    25.     /*当前队列是空的才会触发$.Callback*/
    26.     if ( !startLength && hooks ) {
    27.         hooks.empty.fire();
    28.     }
    29. },

    我们可以发现,只有当前的队列里面是空的才会触发$.Callback,因此要多dequeue一次。

    我们可以合理的利用next(next其实相当于dequeue),然后队列只需要dequeue一次,后面自动连起来,就像鞭炮一样,一旦引燃了,后面就自动点燃,代码如下:

    1. var obj={'name':'nb'},
    2.     f1=function(next,hook){
    3.         console.log('dequeue f1');
    4.         hook.empty.add(function(){
    5.             console.log('f1 end');
    6.         });
    7.         next();
    8.     },
    9.     f2=function(next,hook){
    10.         console.log('dequeue f2');
    11.         hook.empty.add(function(){
    12.         console.log('f2 end');
    13.         });
    14.         next();
    15.     }
    16. $.queue(obj,'a',f1);
    17. $.queue(obj,'a',f2);
    18. $.dequeue(obj,'a');

    我们可以看到,队列中最后一个方法执行next为的是执行接下来的$.Callback。

    还有一点要注意,如果是用'fx'作为队列名,那么在dequeue的时候可以不用指定是哪个队列,因为'fx'是默认队列名,直接$.dequeue(obj)就好了。

    1. var obj={'name':'nb'},
    2.     f1=function(next,hook){
    3.     console.log('dequeue f1');
    4.     hook.empty.add(function(){
    5.         console.log('f1 end');
    6.     });
    7.     next();
    8. },
    9. f2=function(next,hook){
    10.     console.log('dequeue f2');
    11.     hook.empty.add(function(){
    12.         console.log('f2 end');
    13.     });
    14.     next();
    15. }
    16. $.queue(obj,'fx',f1);
    17. $.queue(obj,'fx',f2);
    18. $.dequeue(obj);

    $.fn.queue

    这个和$.queue不同就是这个要配合元素一起使用,例如$(el).queue(name,fn)。不过在内部实现上,$.queue和$.fn.queue还是有区别的,我们先来看看下面的例子:

    1. //$.queue
    2. $.queue($('div').eq(0),'a',function(){});
    3. //$.fn.queue
    4. $('div').eq(0).queue('b',function(){});
    5. //试问这两者有没有区别

    如果不是很了解内部逻辑还真的说不上来有什么不同,其实这两者最大的不同就是绑定的对象不同:

    也就是说$.queue($('div').eq(0),'a',f1);根本没有把队列绑定在$('div').eq(0)这个真正的对象上,而是绑定在了$('div').eq(0)返回这个数组上了!

    如果想让$.queue绑定到具体的DOM元素上,可以这样:$.queue($('div').eq(0)[0],'a',f1),如下图所示:

    $.animate中也有使用队列,它使用的是默认队列'fx',我们可以指定其他的队列名,用dequeue触发,或者不使用队列,具体请看下面的demo:

    $.animate的queue

    我们可以发现如果$.animate不使用队列,那么多个动画是同时进行的。

    $.fn.delay

    这个是延迟队列执行的,简单demo如下:

    1. var $div=$('div').eq(0);
    2. $div.delay(1000,'a');
    3. $div.queue('a',function(){console.log('ok');});
    4. $div.dequeue('a');

    这个就好比在队列中放了一个延迟执行dequeue的方法。看看delay的源代码:

    1. delay: function(e, t) {
    2.     return e = st.fx ? st.fx.speeds[e] || e : e,
    3.     t = t || "fx",
    4.     this.queue(t, function(t, n) {
    5.         var r = setTimeout(t, e);
    6.         n.stop = function() {
    7.             clearTimeout(r)
    8.         }
    9.     })
    10. },

    可以发现其实delay的就是延迟执行了dequeue而已。

    $.fn.promise

    这个就比较厉害了,通过刚刚我们知道,给一个对象上添加一个队列,可以通过hooks添加回调方法,但是如果同时有很多对象都有队列,那么如何知道最后一个队列完成呢?这就是promise做的,我们先来看一个demo:

    queue的promise

    promise其实是吧所有的队列中的hooks里面添加了一个检查是否所有队列完成的一个方法,然后自己返回的是一个Deferred对象的promise方法。只要所有的执行完毕,那么就会执行Deferred后面添加then或者done方法。$.Deferred的使用可以看: $.Deferred源码分析

    看看promise的源码就知道了:

    1. promise: function( type, obj ) {
    2.     var tmp,
    3.         count = 1,
    4.         defer = jQuery.Deferred(),
    5.         elements = this,
    6.         i = this.length,
    7.         resolve = function() {
    8.             if ( !( --count ) ) {
    9.                 defer.resolveWith( elements, [ elements ] );
    10.             }
    11.         };
    12.         if ( typeof type !== "string" ) {
    13.             obj = type;
    14.             type = undefined;
    15.         }
    16.         type = type || "fx";
    17.         while ( i-- ) {
    18.             tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
    19.             if ( tmp && tmp.empty ) {
    20.                 count++;
    21.                 tmp.empty.add( resolve );
    22.             }
    23.         }
    24.         resolve();
    25.     return defer.promise( obj );
    26. }

    如果说promise的对象没有队列怎么办?那么promise返回的Deferred对象就会立即执行,例如:

    1. $('div').promise().then(function(){
    2.     console.log('ok');
    3. });
    回到顶部
    我要评论

    所有评论

      相关文章