jQuery中的队列是什么?

388

我发现 jQuery.com 上 queue()/dequeue() 文档太简单了,难以理解。那么在 jQuery 中,队列(queues)到底是什么?我应该如何使用它们呢?


3
使用队列解决问题的一个好例子:https://dev59.com/UG435IYBdhLWcg3wvy-_#5230395 - gnarf
5个回答

491

jQuery的.queue().dequeue()的用途

在jQuery中,队列被用于动画。您可以将它们用于任何目的。它们是基于每个元素存储的函数数组,使用jQuery.data()。它们是先进先出(FIFO)的。您可以通过调用.queue()向队列添加一个函数,并使用.dequeue()(通过调用)来删除函数。

要理解jQuery队列函数的内部工作方式,阅读源代码并查看示例对我非常有帮助。其中最好的队列函数示例之一是.delay()

$.fn.delay = function( time, type ) {
  time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
  type = type || "fx";

  return this.queue( type, function() {
    var elem = this;
    setTimeout(function() {
      jQuery.dequeue( elem, type );
    }, time );
  });
};

默认队列 - fx

jQuery中的默认队列是fx。 默认队列具有一些与其他队列不共享的特殊属性。

  1. 自动开始:在调用$(elem).queue(function(){});时,如果队列尚未启动,则fx队列将自动dequeue下一个函数并运行它。
  2. 'inprogress'标志:每当您从fx队列中dequeue()一个函数时,它都会unshift()(将其推入数组的第一个位置)字符串"inprogress",以标记队列当前正在运行。
  3. 这是默认值! fx队列默认用于.animate()和所有调用它的函数。

注意:如果您使用自定义队列,则必须手动.dequeue()函数,它们不会自动启动!

检索/设置队列

您可以通过调用没有函数参数的.queue()来检索对jQuery队列的引用。 如果要查看队列中有多少项,则可以使用该方法。 您可以使用pushpopunshiftshift在原地操作队列。 您可以通过将数组传递给.queue()函数来替换整个队列。

快速示例:

// lets assume $elem is a jQuery object that points to some element we are animating.
var queue = $elem.queue();
// remove the last function from the animation queue.
var lastFunc = queue.pop(); 
// insert it at the beginning:    
queue.unshift(lastFunc);
// replace queue with the first three items in the queue
$elem.queue(queue.slice(0,3)); 

一个动画(fx)队列示例:

在jsFiddle上运行示例

$(function() {
    // lets do something with google maps:
    var $map = $("#map_canvas");
    var myLatlng = new google.maps.LatLng(-34.397, 150.644);
    var myOptions = {zoom: 8, center: myLatlng, mapTypeId: google.maps.MapTypeId.ROADMAP};
    var geocoder = new google.maps.Geocoder();
    var map = new google.maps.Map($map[0], myOptions);
    var resized = function() {
        // simple animation callback - let maps know we resized
        google.maps.event.trigger(map, 'resize');
    };

    // wait 2 seconds
    $map.delay(2000);
    // resize the div:
    $map.animate({
        width: 250,
        height: 250,
        marginLeft: 250,
        marginTop:250
    }, resized);
    // geocode something
    $map.queue(function(next) {
        // find stackoverflow's whois address:
      geocoder.geocode({'address': '55 Broadway New York NY 10006'},handleResponse);

      function handleResponse(results, status) {
          if (status == google.maps.GeocoderStatus.OK) {
              var location = results[0].geometry.location;
              map.setZoom(13);
              map.setCenter(location);
              new google.maps.Marker({ map: map, position: location });
          }
          // geocoder result returned, continue with animations:
          next();
      }
    });
    // after we find stack overflow, wait 3 more seconds
    $map.delay(3000);
    // and resize the map again
    $map.animate({
        width: 500,
        height: 500,
        marginLeft:0,
        marginTop: 0
    }, resized);
});

另一个自定义队列示例

在 jsFiddle 上运行示例

var theQueue = $({}); // jQuery on an empty object - a perfect queue holder

$.each([1,2,3],function(i, num) {
  // lets add some really simple functions to a queue:
  theQueue.queue('alerts', function(next) { 
    // show something, and if they hit "yes", run the next function.
    if (confirm('index:'+i+' = '+num+'\nRun the next function?')) {
      next();
    }
  }); 
});

// create a button to run the queue:
$("<button>", {
  text: 'Run Queue', 
  click: function() { 
    theQueue.dequeue('alerts'); 
  }
}).appendTo('body');

// create a button to show the length:
$("<button>", {
  text: 'Show Length', 
  click: function() { 
    alert(theQueue.queue('alerts').length); 
  }
}).appendTo('body');

排队Ajax调用:

我开发了一个$.ajaxQueue()插件,它使用$.Deferred.queue()$.ajax(),并且还返回一个promise,当请求完成时解决。另一个版本的$.ajaxQueue仍然适用于1.4,可以在我的答案Sequencing Ajax Requests中找到。

/*
* jQuery.ajaxQueue - A queue for ajax requests
* 
* (c) 2011 Corey Frang
* Dual licensed under the MIT and GPL licenses.
*
* Requires jQuery 1.5+
*/ 
(function($) {

// jQuery on an empty object, we are going to use this as our Queue
var ajaxQueue = $({});

$.ajaxQueue = function( ajaxOpts ) {
    var jqXHR,
        dfd = $.Deferred(),
        promise = dfd.promise();

    // queue our ajax request
    ajaxQueue.queue( doRequest );

    // add the abort method
    promise.abort = function( statusText ) {

        // proxy abort to the jqXHR if it is active
        if ( jqXHR ) {
            return jqXHR.abort( statusText );
        }

        // if there wasn't already a jqXHR we need to remove from queue
        var queue = ajaxQueue.queue(),
            index = $.inArray( doRequest, queue );

        if ( index > -1 ) {
            queue.splice( index, 1 );
        }

        // and then reject the deferred
        dfd.rejectWith( ajaxOpts.context || ajaxOpts,
            [ promise, statusText, "" ] );

        return promise;
    };

    // run the actual query
    function doRequest( next ) {
        jqXHR = $.ajax( ajaxOpts )
            .done( dfd.resolve )
            .fail( dfd.reject )
            .then( next, next );
    }

    return promise;
};

})(jQuery);

我现在已经将这篇文章添加到learn.jquery.com上了,那个网站上还有其他关于队列的精彩文章,去看看吧。


3
在Ajax队列示例中可以看到,实际上可以将队列事件附加到一个空对象$({}) - gnarf
3
这篇摘要非常有用。我刚刚完成了一个懒加载器,可以延迟请求屏幕底部以下的重内容,直到滚动到视野中才加载。使用 jQuery 的 queue() 使得这些 Ajax 请求非常顺畅(即使你直接跳转到页面底部)。谢谢! - Jeff Standen
14
很高兴发现你仍在为更新版本的jQuery进行更新。赞一个 :) - Shaz
3
对于那些刚学习队列和 Promise 等技术的人,我想补充一点内容 - 在 ajaxQueue 的示例中,调用 $.ajaxQueue() 并将你希望排队的 Ajax 请求放入括号内,它将返回一个 Promise。等待队列为空的方法是通过 promise.done(function(){ alert("done")})。我花了一个小时才找到这个方法,希望这篇翻译能帮助其他人节省时间! - Ross
用户们请注意,ajax队列代码非常糟糕。它可能只是为了说明某些问题而存在。 - Benjamin Gruenbaum
显示剩余3条评论

43
为了理解队列方法,你需要了解jQuery如何进行动画。如果你在多个animate方法调用后面写上,jQuery会创建一个“内部”队列并将这些方法调用添加到其中。然后它会逐一运行这些animate调用。
考虑以下代码。
function nonStopAnimation()
{
    //These multiple animate calls are queued to run one after
    //the other by jQuery.
    //This is the reason that nonStopAnimation method will return immeidately
    //after queuing these calls. 
    $('#box').animate({ left: '+=500'}, 4000);
    $('#box').animate({ top: '+=500'}, 4000);
    $('#box').animate({ left: '-=500'}, 4000);

    //By calling the same function at the end of last animation, we can
    //create non stop animation. 
    $('#box').animate({ top: '-=500'}, 4000 , nonStopAnimation);
}

“queue”/“dequeue”方法让您控制此“动画队列”。

默认情况下,动画队列名为“fx”。我在这里创建了一个示例页面,其中包含各种示例,可说明如何使用队列方法。

http://jsbin.com/zoluge/1/edit?html,output

上面示例页面的代码:
$(document).ready(function() {
    $('#nonStopAnimation').click(nonStopAnimation);

    $('#stopAnimationQueue').click(function() {
        //By default all animation for particular 'selector'
        //are queued in queue named 'fx'.
        //By clearning that queue, you can stop the animation.
        $('#box').queue('fx', []);
    });

    $('#addAnimation').click(function() {
        $('#box').queue(function() {
            $(this).animate({ height : '-=25'}, 2000);
            //De-queue our newly queued function so that queues
            //can keep running.
            $(this).dequeue();
        });
    });

    $('#stopAnimation').click(function() {
        $('#box').stop();
    });

    setInterval(function() {
        $('#currentQueueLength').html(
         'Current Animation Queue Length for #box ' + 
          $('#box').queue('fx').length
        );
    }, 2000);
});

function nonStopAnimation()
{
    //These multiple animate calls are queued to run one after
    //the other by jQuery.
    $('#box').animate({ left: '+=500'}, 4000);
    $('#box').animate({ top: '+=500'}, 4000);
    $('#box').animate({ left: '-=500'}, 4000);
    $('#box').animate({ top: '-=500'}, 4000, nonStopAnimation);
}

现在你可能会问,我为什么要费心处理这个队列?通常情况下,你不需要。但如果你想控制一个复杂的动画序列,那么队列/出队方法就是你的好朋友。
此外,还可以查看jQuery小组关于创建复杂动画序列的有趣讨论。

http://groups.google.com/group/jquery-en/browse_thread/thread/b398ad505a9b0512/f4f3e841eab5f5a2?lnk=gst

动画演示:

http://www.exfer.net/test/jquery/tabslide/

如果你还有问题,请告诉我。


20

在队列中实现多对象动画

这是一个关于如何在队列中实现多个对象动画的简单示例。

Jquery允许我们只对一个对象进行队列操作。但是我们可以在动画函数中访问其他对象。在这个例子中,我们将动画对象 #box1 和 #box2 加入到 #q 对象构建的队列中进行队列操作。

将队列看作函数数组,你就可以像操作数组一样操纵队列。你可以使用 push、pop、unshift、shift 等方法来操作队列。在这个例子中,我们从动画队列中移除最后一个函数并将其插入到开始位置。

当完成操作后,我们通过 dequeue() 函数开始执行动画队列。

在 jsFiddle 上查看代码

html:

  <button id="show">Start Animation Queue</button>
  <p></p>
  <div id="box1"></div>
  <div id="box2"></div>
  <div id="q"></div>

js:

->

JavaScript:

$(function(){

 $('#q').queue('chain',function(next){  
      $("#box2").show("slow", next);
  });


  $('#q').queue('chain',function(next){  
      $('#box1').animate(
          {left: 60}, {duration:1000, queue:false, complete: next}
      )
  });    


  $('#q').queue('chain',function(next){  
      $("#box1").animate({top:'200'},1500, next);
  });


  $('#q').queue('chain',function(next){  
      $("#box2").animate({top:'200'},1500, next);
  });


  $('#q').queue('chain',function(next){  
      $("#box2").animate({left:'200'},1500, next);
  });

  //notice that show effect comes last
  $('#q').queue('chain',function(next){  
      $("#box1").show("slow", next);
  });

});

$("#show").click(function () {
    $("p").text("Queue length is: " + $('#q').queue("chain").length);

    // remove the last function from the animation queue.
    var lastFunc = $('#q').queue("chain").pop();
    // insert it at the beginning:    
    $('#q').queue("chain").unshift(lastFunc);

    //start animation queue
    $('#q').dequeue('chain');
});

css:

        #box1 { margin:3px; width:40px; height:40px;
                position:absolute; left:10px; top:60px; 
                background:green; display: none; }
        #box2 { margin:3px; width:40px; height:40px;
                position:absolute; left:100px; top:60px; 
                background:red; display: none; }
        p { color:red; }  

15

它允许您排队动画......例如,而不是这样

$('#my-element').animate( { opacity: 0.2, width: '100px' }, 2000);

同时淡化元素并将其宽度设置为100像素。使用队列可以使动画有序进行,一个动画完成后再进行另一个。

$("#show").click(function () {
    var n = $("div").queue("fx");
    $("span").text("Queue length is: " + n.length);
});

function runIt() {
    $("div").show("slow");
    $("div").animate({left:'+=200'},2000);
    $("div").slideToggle(1000);
    $("div").slideToggle("fast");
    $("div").animate({left:'-=200'},1500);
    $("div").hide("slow");
    $("div").show(1200);
    $("div").slideUp("normal", runIt);
}
runIt();

这是从 http://docs.jquery.com/Effects/queue 获取的示例。


这是不正确的。当你有多个 'animate' 调用时,jQuery会将它们放入队列中逐个执行。现在通过使用queue方法,你可以访问该队列并进行必要的操作。 - SolutionYogi
1
@SolutionYogi - 如果您觉得我的答案不正确,请编辑它 - 答案已经被标记为 CW,您拥有足够的声望。 - alex

8

这个帖子对我的问题帮助很大,但我用了不同的方法来使用 $.queue,并且想在这里发布我想到的内容。我需要触发一系列事件(帧),但是序列需要动态构建。我有一个可变数量的占位符,每个占位符都应该包含一系列图像的动画序列。数据保存在一个数组中,所以我通过循环遍历数组来为每个占位符构建每个序列,如下:

/* create an empty queue */
var theQueue = $({});
/* loop through the data array */
for (var i = 0; i < ph.length; i++) {
    for (var l = 0; l < ph[i].length; l++) {
        /* create a function which swaps an image, and calls the next function in the queue */
        theQueue.queue("anim", new Function("cb", "$('ph_"+i+"' img').attr('src', '/images/"+i+"/"+l+".png');cb();"));
        /* set the animation speed */
        theQueue.delay(200,'anim');
    }
}
/* start the animation */
theQueue.dequeue('anim');

这是我得出的脚本的简化版本,但应该展示了原理 - 当一个函数被添加到队列中时,它是使用Function构造函数添加的 - 这样函数可以使用循环中的变量动态编写。请注意函数如何传递next()调用的参数,并在最后调用它。在这种情况下,函数没有时间依赖性(它不使用$.fadeIn或类似的东西),因此我使用$.delay来分步执行帧。


$.queue 基本上是将一个 push 到存储在 $.data 中的数组中,这就是为什么你必须手动告诉它使用 cb() 执行下一个函数。我的理解正确吗? - eighteyes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接