【问题标题】:Queue ajax requests using jQuery.queue()使用 jQuery.queue() 对 ajax 请求进行排队
【发布时间】:2011-06-14 17:13:20
【问题描述】:

我是第一次使用 jQuery.queue(),还没有完全掌握。 有人可以指出我做错了什么吗?

查看萤火虫我仍然看到我的 POST 请求同时触发 - 所以我想知道我是否在错误的地方调用 dequeue()。

另外 - 我怎样才能得到队列长度?

我需要对这些请求进行排队的原因是它会在单击按钮时被触发。并且用户可以快速连续点击多个按钮。

试图剥离我的代码的基本结构:

$("a.button").click(function(){
   $(this).doAjax(params);
});

// method
doAjax:function(params){ 

   $(document).queue("myQueueName", function(){
     $.ajax({
       type: 'POST',
       url: 'whatever.html',
       params: params,
       success: function(data){
         doStuff;

         $(document).dequeue("myQueueName");
       }
     });
   });

}

【问题讨论】:

  • 这个answer,也在 StackOverflow 上,提供了一组很好的示例,包括它与 ajax 调用的使用。
  • 我确实尝试过这种方法 - 但我无法让它发挥作用。我看到它没有使用 dequeue() 并且想知道这是否是我的问题?
  • 我猜 next() 和 dequeue 做同样的事情?
  • 是的,next() 做同样的事情。当你的函数被调用时,它由队列逻辑传入。

标签: jquery ajax queue


【解决方案1】:

这里的问题是,.ajax() 会触发异步运行的 Ajax 请求。这意味着,.ajax() 立即返回,非阻塞。因此,您将函数排入队列,但它们几乎会像您描述的那样同时触发。

我不认为.queue() 是一个有ajax 请求的好地方,它更适合fx methods 的使用。你需要一个简单的经理。

var ajaxManager = (function() {
     var requests = [];

     return {
        addReq:  function(opt) {
            requests.push(opt);
        },
        removeReq:  function(opt) {
            if( $.inArray(opt, requests) > -1 )
                requests.splice($.inArray(opt, requests), 1);
        },
        run: function() {
            var self = this,
                oriSuc;

            if( requests.length ) {
                oriSuc = requests[0].complete;

                requests[0].complete = function() {
                     if( typeof(oriSuc) === 'function' ) oriSuc();
                     requests.shift();
                     self.run.apply(self, []);
                };   

                $.ajax(requests[0]);
            } else {
              self.tid = setTimeout(function() {
                 self.run.apply(self, []);
              }, 1000);
            }
        },
        stop:  function() {
            requests = [];
            clearTimeout(this.tid);
        }
     };
}());

这远非完美,我只是想示范一下要走的路。上面的例子可以这样使用

$(function() {
    ajaxManager.run(); 

    $("a.button").click(function(){
       ajaxManager.addReq({
           type: 'POST',
           url: 'whatever.html',
           data: params,
           success: function(data){
              // do stuff
           }
       });
    });
});

【讨论】:

  • 好的 - 我明白他们为什么要立即开火了,谢谢。我将研究您对处理程序的建议。你什么时候会建议像@nathan 建议的那样只使用现有的插件,或者编写这样的处理程序?
  • @MBax:我不知道这个插件,但我总是喜欢自己做事,因为灵活性、知识和冷静:-)
  • @MBax:它只是让整个事情保持活力。如果当前没有排队的请求,它只是空闲并每秒调用一次。要关闭整个事情,请致电ajaxManager.stop();
  • 只是想对上面的代码稍加修正,对于那些试图实际使用上面代码但对他们不起作用的人。 “params: params”实际上应该是实际请求中的“data: params”。我知道这最初是在 @MBax 的代码中,但认为大多数人会在这里寻找解决方案。
  • 这是一个很棒的代码,但我有一些疑问; ajax 管理器似乎解决了我的问题。我有两种类型的请求:1)在选择更改时; 2) 在更改输入时,我有一个调用大量 add() 的 forEach。如果我在 add() 之后使用 stop(),则不会发送请求,否则如果我不使用 stop() 或在 add() 之前使用,则计时器将永远存在。在所有请求后如何停止?
【解决方案2】:

我发现上述解决方案有点复杂,而且我需要在发送之前更改请求(以更新新的数据令牌)。

所以我把这个放在一起。来源:https://gist.github.com/2470554

/* 

Allows for ajax requests to be run synchronously in a queue

Usage::

var queue = new $.AjaxQueue();

queue.add({
  url: 'url',
  complete: function() {
    console.log('ajax completed');
  },
  _run: function(req) {
    //special pre-processor to alter the request just before it is finally executed in the queue
    req.url = 'changed_url'
  }
});

*/

$.AjaxQueue = function() {
  this.reqs = [];
  this.requesting = false;
};
$.AjaxQueue.prototype = {
  add: function(req) {
    this.reqs.push(req);
    this.next();
  },
  next: function() {
    if (this.reqs.length == 0)
      return;

    if (this.requesting == true)
      return;

    var req = this.reqs.splice(0, 1)[0];
    var complete = req.complete;
    var self = this;
    if (req._run)
      req._run(req);
    req.complete = function() {
      if (complete)
        complete.apply(this, arguments);
      self.requesting = false;
      self.next();
    }

    this.requesting = true;
    $.ajax(req);
  }
};

【讨论】:

  • 请将代码(或至少相关部分)也放在这里。据我们所知,github 可能明天就宕机了,再也回不来了,这篇文章将变得毫无意义。
  • 哇。真的吗? “据我们所知,github 明天可能会关闭”
【解决方案3】:

我需要做类似的事情,所以我想在这里发布我的解决方案。

基本上,我得到的是一个页面,其中列出了货架上的项目,这些项目都有不同的标准。我想一个一个地加载书架,而不是全部加载,以便更快地向用户提供一些内容,让他们在加载其余内容时可以查看这些内容。

基本上我将每个架子的 ID 存储在一个 JS 数组中,当我从 PHP 调用它们时使用它。

然后我创建了一个递归函数,每次调用它时都会从数组中弹出第一个索引,并为弹出的 id 请求货架。一旦我得到来自$.get()$.post() 的响应,无论我喜欢使用哪个,我都会从回调中调用递归函数。

以下是代码的详细说明:

// array of shelf IDs
var shelves = new Array(1,2,3,4);

// the recursive function
function getShelfRecursive() {

    // terminate if array exhausted
    if (shelves.length === 0)
        return;

    // pop top value
    var id = shelves[0];
    shelves.shift();

    // ajax request
    $.get('/get/shelf/' + id, function(){
         // call completed - so start next request
         getShelfRecursive();
    });
}

// fires off the first call
getShelfRecursive();

【讨论】:

  • 要弹出最高值,您可以使用var id = shelves.pop();。那么你就不需要明确的.shift();
  • 或者如果你想要.shift()结尾,你可以使用var id = shelves.shift();
【解决方案4】:

我还必须在我拥有的解决方案中这样做,我发现我可以这样做:

//A variable for making sure to wait for multiple clicks before emptying.
var waitingTimeout; 

$("a.button").click(function(){
   $(this).doAjax(params);
   clearTimeout(waitingTimeout);
   waitingTimeout = setTimeout(function(){noMoreClicks();},1000);
});

// method
doAjax:function(params){ 

   $(document).queue("myQueueName", function(next){
     $.ajax({
       type: 'POST',
       url: 'whatever.html',
       data: params,
       contentType: "application/json; charset=utf-8",
       dataType: "json",
       success: function(data){
         doStuff;
         next();
       },
       failure: function(data){
         next();
       },
       error: function(data){
         next();
       }
     });
   });

}

function noMoreClicks(){
    $(document).dequeue("myQueueName");
}

通过使用队列函数中传递的next() 回调,您可以将下一个操作出队。因此,通过将 next 放入 ajax 的处理程序中,您可以有效地使 ajax 调用与浏览器和浏览器的渲染或绘制线程异步,但使它们彼此同步或序列化。

Here is a very basic example. 在示例小提琴中。单击按钮一次,然后等待一秒钟。您将看到超时触发和单个操作发生。接下来以尽可能快的速度(或快于一秒)单击按钮,您将看到在您单击按钮的所有时间中,操作都在排队,然后仅在等待一秒钟后,它们才会点击页面并在之后淡入另一个。

这样做的好处在于,如果队列已经清空,则在清空时添加到它的任何操作都会放在最后,然后在时机成熟时进行处理。

【讨论】:

    【解决方案5】:

    我需要为未知数量的 ajax 调用执行此操作。答案是将每个推入一个数组,然后使用:

    $.when.apply($, arrayOfDeferreds).done(function () {
        alert("All done");
    });
    

    【讨论】:

    • 当 ajax 调用被推送到数组时不要立即触发。例如,var arr = []; arr.push($.get(...)); 会立即触发 GET 调用吗?
    • 你能举一个更完整的例子吗?
    【解决方案6】:

    learn.jquery.com 网站have a good example too:

    // jQuery on an empty object, we are going to use this as our queue
    var ajaxQueue = $({});
    
    $.ajaxQueue = function(ajaxOpts) {
      // Hold the original complete function
      var oldComplete = ajaxOpts.complete;
    
      // Queue our ajax request
      ajaxQueue.queue(function(next) {
        // Create a complete callback to invoke the next event in the queue
        ajaxOpts.complete = function() {
          // Invoke the original complete if it was there
          if (oldComplete) {
            oldComplete.apply(this, arguments);
          }
    
          // Run the next query in the queue
          next();
        };
    
        // Run the query
        $.ajax(ajaxOpts);
      });
    };
    
    // Get each item we want to copy
    $("#items li").each(function(idx) {
      // Queue up an ajax request
      $.ajaxQueue({
        url: "/ajax_html_echo/",
        data: {
          html: "[" + idx + "] " + $(this).html()
        },
        type: "POST",
        success: function(data) {
          // Write to #output
          $("#output").append($("<li>", {
            html: data
          }));
        }
      });
    });
    

    【讨论】:

      【解决方案7】:

      你可以扩展 jQuery:

      (function($) {
        // Empty object, we are going to use this as our Queue
        var ajaxQueue = $({});
      
        $.ajaxQueue = function(ajaxOpts) {
          // hold the original complete function
          var oldComplete = ajaxOpts.complete;
      
          // queue our ajax request
          ajaxQueue.queue(function(next) {    
      
            // create a complete callback to fire the next event in the queue
            ajaxOpts.complete = function() {
              // fire the original complete if it was there
              if (oldComplete) oldComplete.apply(this, arguments);    
              next(); // run the next query in the queue
            };
      
            // run the query
            $.ajax(ajaxOpts);
          });
        };
      
      })(jQuery);
      

      然后像这样使用它:

      $.ajaxQueue({
          url: 'doThisFirst.php',
          async: true,
          success: function (data) {
              //success handler
          },
          error: function (jqXHR,textStatus,errorThrown) {
              //error Handler
          }       
      });
      $.ajaxQueue({
          url: 'doThisSecond.php',
          async: true,
          success: function (data) {
              //success handler
          },
          error: function (jqXHR,textStatus,errorThrown) {
              //error Handler
          }       
      });
      

      当然,您可以使用任何其他 $.ajax 选项,例如 type、data、contentType、DataType,因为我们正在扩展 $.ajax

      【讨论】:

      • Rad 解决方案。为了测试排队,我在我的 PHP 代码中添加了 1 秒睡眠,并尽可能多地排队 AJAX 请求。每个都按预期完成。在它跳闸并错过几次保存之前。很酷。
      • 完美!是否可以知道队列何时为空或正在运行?
      【解决方案8】:

      另一个版本的 jAndy 答案,没有计时器。

      var ajaxManager = {
          requests: [],
          addReq: function(opt) {
              this.requests.push(opt);
      
              if (this.requests.length == 1) {
                  this.run();
              }
          },
          removeReq: function(opt) {
              if($.inArray(opt, requests) > -1)
                  this.requests.splice($.inArray(opt, requests), 1);
          },
          run: function() {
              // original complete callback
              oricomplete = this.requests[0].complete;
      
              // override complete callback
              var ajxmgr = this;
              ajxmgr.requests[0].complete = function() {
                   if (typeof oricomplete === 'function')
                      oricomplete();
      
                   ajxmgr.requests.shift();
                   if (ajxmgr.requests.length > 0) {
                      ajxmgr.run();
                   }
              };
      
              $.ajax(this.requests[0]);
          },
          stop: function() {
              this.requests = [];
          },
      }
      

      使用:

      $(function() {
          $("a.button").click(function(){
             ajaxManager.addReq({
                 type: 'POST',
                 url: 'whatever.html',
                 data: params,
                 success: function(data){
                    // do stuff
                 }
             });
          });
      });
      

      【讨论】:

      【解决方案9】:

      这是我的解决方案,我用它来生成一些 Browsergame 的请求队列。 如果发生任何事情,我会停止此队列并通过一些特殊的最后请求或清理来完成工作。

      var get_array = ["first", "second", "third"];
      
      var worker = $("<div />"); // to line up requests in queue
      $.queuedAjax = function(args){  // add up requests for me       
          worker.queue(
              function(next){
                  $.ajax(args).always(next);            
              }
          );
        };
      
      $.queuedSomething = function(){ // add up something special for me
          worker.queue(
              function(next){
                  //worker.clearQueue();
                  //worker = $("<div />"); //cleanup for next .each
                  //maybe another .each           
              }
          );
        };
      
      $.each( get_array , function( key , value ) {
        $.queuedAjax({
          type: 'GET',
          url: '/some.php?get='+value,
          dataType: 'text',
          success: function(sourcecode){
      
              if (sourcecode.match(/stop your requests, idiot!/)) {   
                  worker.clearQueue().queue($.queuedSomething);
                  alert(' the server told me to stop. i stopped all but not the last ´$.queuedSomething()´ ');
              }
      
          }
        });           
      }); 
      $.queuedSomething();
      

      【讨论】:

        【解决方案10】:

        我使用这个非常简单的代码来防止 ajax 调用相互“超越”。

        var dopostqueue = $({});
        function doPost(string, callback)
        {
            dopostqueue.queue(function()
            {
                $.ajax(
                {   
                    type: 'POST',
                    url: 'thephpfile.php',
                    datatype: 'json',
                    data: string,
                    success:function(result) 
                    {
                        dopostqueue.dequeue();
                        callback(JSON.parse(result));
                    }
                })
            });
        }
        

        如果您不希望队列自行处理,您可以从函数中删除 dequeue 并从另一个函数中调用它。 至于获取队列长度,在本例中为:

        dopostqueue.queue().length
        

        【讨论】:

          【解决方案11】:

          这只是我为 nodejs 编写的多线程队列运行器的另一个示例。您可以将其调整为 jquery 或 angular。每个 API 中的 Promise 略有不同。我已将此模式用于诸如通过创建多个查询以获取所有数据并一次允许 6 个查询来从 SharePoint 中的大型列表中提取所有项目,以避免服务器强加的限制。

          /*
              Job Queue Runner (works with nodejs promises): Add functions that return a promise, set the number of allowed simultaneous threads, and then run
              (*) May need adaptation if used with jquery or angular promises
          
              Usage:
                  var sourcesQueue = new QueueRunner('SourcesQueue');
                  sourcesQueue.maxThreads = 1;
                  childSources.forEach(function(source) {
                      sourcesQueue.addJob(function() { 
                          // Job function - perform work on source
                      });
                  }
                  sourcesQueue.run().then(function(){
                      // Queue complete...
                  });
          */
          var QueueRunner = (function () {
              function QueueRunner(id) {
                  this.maxThreads = 1; // Number of allowed simultaneous threads
                  this.jobQueue = [];
                  this.threadCount = 0;
                  this.jobQueueConsumer = null;
                  this.jobsStarted = 0;
                  if(typeof(id) !== 'undefined') {
                      this.id = id;
                  }
                  else {
                      this.id = 'QueueRunner';
                  }
              }    
              QueueRunner.prototype.run = function () {
                  var instance = this;        
                  return new Promise(function(resolve, reject) {
                      instance.jobQueueConsumer = setInterval(function() {
                          if(instance.threadCount < instance.maxThreads && instance.jobQueue.length > 0) {
                              instance.threadCount++;
                              instance.jobsStarted++;
                              // Remove the next job from the queue (index zero) and run it
                              var job = instance.jobQueue.splice(0, 1)[0];
                              logger.info(instance.id + ': Start job ' + instance.jobsStarted + ' of ' + (instance.jobQueue.length + instance.jobsStarted));
                              job().then(function(){
                                  instance.threadCount--;
                              }, function(){
                                  instance.threadCount--;
                              });
                          }
                          if(instance.threadCount < 1 && instance.jobQueue.length < 1) {
                              clearInterval(instance.jobQueueConsumer);
                              logger.info(instance.id + ': All jobs done.');
                              resolve();
                          }
                      }, 20);
                  });     
              };
              QueueRunner.prototype.addJob = function (func) {
                  this.jobQueue.push(func);
              };
              return QueueRunner;
          }());
          

          【讨论】:

            【解决方案12】:

            使用提供可观察支持的框架,例如knockout.js,您可以实现一个观察队列,当将其推送到该队列时,该队列会将呼叫排入队列,并且轮班将处理该过程。

            淘汰赛实现如下所示:

            var ajaxQueueMax = 5;
            self.ajaxQueue = ko.observableArray();
            self.ajaxQueueRunning = ko.observable(0);
            
            ko.computed(function () {
              if (self.ajaxQueue().length > 0 && self.ajaxQueueRunning() < ajaxQueueMax) {
                var next = self.ajaxQueue.shift();
                self.ajaxQueueRunning(self.ajaxQueueRunning() + 1);
                $.ajax(next).always(function () {
                  self.ajaxQueueRunning(self.ajaxQueueRunning() - 1);
                });
              }
            });
            

            请注意,我们利用 observables 告诉我们何时应该发送另一个 ajax 请求。这种方法可以以更通用的形式应用。

            例如,假设您有一个检索到大量条目的剔除映射,但您需要为每个项目调用另一个服务来丰富它们,比如设置一个值。

            self.widgets = ko.observableArray();
            
            ko.computed(function () {
              var mapping = {
                create: function (options) {
                  var res = ko.mapping.fromJS(options.data);
                  res.count = ko.observable();
            
                  // widget enrichment.
                  self.ajaxQueue.push({
                    dataType: "json",
                    url: "/api/widgets/" + options.data.id + "/clicks",
                    success: function (data) {
                      res.count(data);
                    }
                  });
                  return res;
                }
              };
            
              // Initial request for widgets
              $.getJSON("/api/widgets", function (data) {
                ko.mapping.fromJS(data, mapping, self.widgets);
              });
            });
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-03-03
              • 2021-08-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-08-28
              相关资源
              最近更新 更多