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

$.queue

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

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

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

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

$.dequeue

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

var obj={'name':'nb'},
f1=function(next,hook){
        console.log('dequeue f1');
        hook.empty.add(function(){
            console.log('f1 end');
        });
},
f2=function(next,hook){
        console.log('dequeue f2');
        hook.empty.add(function(){
            console.log('f2 end');
        });
}
$.queue(obj,'a',f1);
$.queue(obj,'a',f2);
$.dequeue(obj,'a');
$.dequeue(obj,'a');
$.dequeue(obj,'a');
/*
dequeue f1
dequeue f2
f1 end
f2 end
*/

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

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

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

dequeue: function( elem, type ) {
    type = type || "fx";
    var queue = jQuery.queue( elem, type ),
        startLength = queue.length,
        fn = queue.shift(),
        hooks = jQuery._queueHooks( elem, type ),
        next = function() {
            jQuery.dequeue( elem, type );
        };
        // If the fx queue is dequeued, always remove the progress sentinel
        if ( fn === "inprogress" ) {
            fn = queue.shift();
            startLength--;
        }
    if ( fn ) {
        // Add a progress sentinel to prevent the fx queue from being
        // automatically dequeued
        if ( type === "fx" ) {
            queue.unshift( "inprogress" );
        }
        // Clear up the last queue stop function
        delete hooks.stop;
        fn.call( elem, next, hooks );
    }
    /*当前队列是空的才会触发$.Callback*/
    if ( !startLength && hooks ) {
        hooks.empty.fire();
    }
},

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

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

var obj={'name':'nb'},
    f1=function(next,hook){
        console.log('dequeue f1');
        hook.empty.add(function(){
            console.log('f1 end');
        });
        next();
    },
    f2=function(next,hook){
        console.log('dequeue f2');
        hook.empty.add(function(){
        console.log('f2 end');
        });
        next();
    }
$.queue(obj,'a',f1);
$.queue(obj,'a',f2);
$.dequeue(obj,'a');

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

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

var obj={'name':'nb'},
    f1=function(next,hook){
    console.log('dequeue f1');
    hook.empty.add(function(){
        console.log('f1 end');
    });
    next();
},
f2=function(next,hook){
    console.log('dequeue f2');
    hook.empty.add(function(){
        console.log('f2 end');
    });
    next();
}
$.queue(obj,'fx',f1);
$.queue(obj,'fx',f2);
$.dequeue(obj);

$.fn.queue

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

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

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

也就是说$.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如下:

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

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

delay: function(e, t) {
    return e = st.fx ? st.fx.speeds[e] || e : e,
    t = t || "fx",
    this.queue(t, function(t, n) {
        var r = setTimeout(t, e);
        n.stop = function() {
            clearTimeout(r)
        }
    })
},

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

$.fn.promise

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

queue的promise

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

看看promise的源码就知道了:

promise: function( type, obj ) {
    var tmp,
        count = 1,
        defer = jQuery.Deferred(),
        elements = this,
        i = this.length,
        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 );
}

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

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

所有评论

返回
邮箱:
绑定
取消
×

我要评论

回复:

昵称:(昵称不超过20个字)

图片:

邮箱:
绑定邮箱后,若有回复,会邮件通知。
提交
还可以输入500个字