Javascript 对ajax请求排序

Javascript 对ajax请求排序,javascript,jquery,ajax,design-patterns,queue,Javascript,Jquery,Ajax,Design Patterns,Queue,我发现有时需要迭代一些集合,并对每个元素进行ajax调用。我希望在移动到下一个元素之前返回每个调用,这样我就不会因为请求而使服务器崩溃——这通常会导致其他问题。我不想将async设置为false并冻结浏览器 通常,这涉及到设置某种迭代器上下文,我在每次成功回调时都会单步执行。我想一定有更简单的方法 有没有人有一个聪明的设计模式,可以灵活地处理集合,为每个项调用ajax?您可以将所有这些复杂的内容封装到一个函数中,以进行如下简单调用: loadSequantially(['/a', '/a/b',

我发现有时需要迭代一些集合,并对每个元素进行ajax调用。我希望在移动到下一个元素之前返回每个调用,这样我就不会因为请求而使服务器崩溃——这通常会导致其他问题。我不想将async设置为false并冻结浏览器

通常,这涉及到设置某种迭代器上下文,我在每次成功回调时都会单步执行。我想一定有更简单的方法


有没有人有一个聪明的设计模式,可以灵活地处理集合,为每个项调用ajax?

您可以将所有这些复杂的内容封装到一个函数中,以进行如下简单调用:

loadSequantially(['/a', '/a/b', 'a/b/c'], function() {alert('all loaded')});
下面是一个粗略的草图(工作示例,除了ajax调用)。这可以修改为使用类似队列的结构而不是数组

  // load sequentially the given array of URLs and call 'funCallback' when all's done
  function loadSequantially(arrUrls, funCallback) {
     var idx = 0;

     // callback function that is called when individual ajax call is done
     // internally calls next ajax URL in the sequence, or if there aren't any left,
     // calls the final user specified callback function
     var individualLoadCallback = function()   {
        if(++idx >= arrUrls.length) {
           doCallback(arrUrls, funCallback);
        }else {
           loadInternal();
        }
     };

     // makes the ajax call
     var loadInternal = function() {
        if(arrUrls.length > 0)  {
           ajaxCall(arrUrls[idx], individualLoadCallback);
        }else {
           doCallback(arrUrls, funCallback);
        }
     };

     loadInternal();
  };

  // dummy function replace with actual ajax call
  function ajaxCall(url, funCallBack) {
     alert(url)
     funCallBack();
  };

  // final callback when everything's loaded
  function doCallback(arrUrls, func)   {
     try   {
        func();
     }catch(err) {
        // handle errors
     }
  };
我过去常常获得这种功能


正如您所说,我能想到的唯一解决方案是维护一个挂起的呼叫/回调列表。或者将下一个调用嵌套在上一个回调中,但这感觉有点混乱

理想情况下,一个具有多个入口点的协程(corroutine)将是整洁的,这样来自服务器的每个回调都可以调用同一个协程。该死,这将在Javascript 1.7中实现

让我尝试使用闭包

function BlockingAjaxCall (URL,arr,AjaxCall,OriginalCallBack)
{    
     var nextindex = function()
     {
         var i =0;
         return function()
         {
             return i++;
         }
     };

     var AjaxCallRecursive = function(){
             var currentindex = nextindex();
             AjaxCall
             (
                 URL,
                 arr[currentindex],
                 function()
                 {
                     OriginalCallBack();
                     if (currentindex < arr.length)
                     {
                         AjaxCallRecursive();
                     }
                 }
             );
     };
     AjaxCallRecursive();    
}
// suppose you always call Ajax like AjaxCall(URL,element,callback) you will do it this way
BlockingAjaxCall(URL,myArray,AjaxCall,CallBack);
function BlockingAjaxCall(URL、arr、AjaxCall、OriginalCallBack)
{    
var nextindex=函数()
{
var i=0;
返回函数()
{
返回i++;
}
};
var AjaxCallRecursive=函数(){
var currentindex=nextindex();
AjaxCall
(
网址,
arr[currentindex],
函数()
{
原始回调();
if(当前索引
jQuery 1.5+ 我开发了一个插件,该插件使用,并在请求完成时传回一个解决方案

/*
* 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);
jQuery 1.4 如果您使用的是jQuery1.4,那么可以利用空对象上的动画队列为元素的ajax请求创建自己的“队列”

您甚至可以将此因素考虑到您自己的
$.ajax()
替换中。此插件
$.ajaxQueue()
使用jQuery的标准“fx”队列,如果队列尚未运行,它将自动启动第一个添加的元素

(function($) {
  // 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 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);
示例用法 因此,我们有一个
,其中有一些
  • ,我们希望(使用ajax!)复制到

    //获取我们要复制的每个项目
    $(“#项li”)。每个(功能(idx){
    //将ajax请求排队
    $.ajaxQueue({
    url:“/echo/html/”,
    数据:{html:[“+idx+”]“+$(this.html()},
    键入:“POST”,
    成功:功能(数据){
    //写入#输出
    $(“#输出”).append($(“
  • ”,{html:data})); } }); });

  • -

    是的,虽然其他答案也可以,但它们都是大量代码,看起来很凌乱。js的设计就是为了优雅地解决这种情况

    例如,这将导致大多数浏览器挂起:

    for(var i=0; i<1000; i++){
        $.ajax('myserver.api', { data:i, type:'post' });
    }
    

    使用延迟承诺的快速小型解决方案。尽管这使用了jQuery的
    $.Deferred
    ,但任何其他方法都应该这样做

    var Queue = function () {
        var previous = new $.Deferred().resolve();
    
        return function (fn, fail) {
            return previous = previous.then(fn, fail || fn);
        };
    };
    
    用法,调用以创建新队列:

    var queue = Queue();
    
    // Queue empty, will start immediately
    queue(function () {
        return $.get('/first');
    });
    
    // Will begin when the first has finished
    queue(function() {
        return $.get('/second');
    });
    

    请参阅异步请求的并排比较。

    您可以使用
    然后
    实现相同的功能

    var files = [
      'example.txt',
      'example2.txt',
      'example.txt',
      'example2.txt',
      'example.txt',
      'example2.txt',
      'example2.txt',
      'example.txt'
    ];
    
    nextFile().done(function(){
      console.log("done",arguments)
    });
    
    function nextFile(text){
      var file = files.shift();
      if(text)
        $('body').append(text + '<br/>');
      if(file)
        return $.get(file).then(nextFile);
    }
    
    var文件=[
    “example.txt”,
    “example2.txt”,
    “example.txt”,
    “example2.txt”,
    “example.txt”,
    “example2.txt”,
    “example2.txt”,
    “example.txt”
    ];
    nextFile().done(函数()){
    log(“完成”,参数)
    });
    函数nextFile(文本){
    var file=files.shift();
    如果(文本)
    $('body').append(text+'
    '); 如果(文件) 返回$.get(文件).then(下一个文件); }

    我发布这个答案的目的是想在将来帮助其他人,在相同的场景中寻找一些简单的解决方案

    现在,使用ES6中引入的本机promise支持也可以做到这一点。您可以将ajax调用包装成一个承诺,并将其返回给元素的处理程序

    function ajaxPromise(elInfo) {
        return new Promise(function (resolve, reject) {
            //Do anything as desired with the elInfo passed as parameter
    
            $.ajax({
                type: "POST",
                url: '/someurl/',
                data: {data: "somedata" + elInfo},
                success: function (data) {
                    //Do anything as desired with the data received from the server,
                    //and then resolve the promise
                    resolve();
                },
                error: function (err) {
                    reject(err);
                },
                async: true
            });
    
        });
    }
    
    现在递归地调用函数,从这里可以得到元素的集合

    function callAjaxSynchronous(elCollection) {
        if (elCollection.length > 0) {
            var el = elCollection.shift();
            ajaxPromise(el)
            .then(function () {
                callAjaxSynchronous(elCollection);
            })
            .catch(function (err) {
                //Abort further ajax calls/continue with the rest
                //callAjaxSynchronous(elCollection);
            });
        }
        else {
            return false;
        }
    }
    

    我建议采用一种更为复杂的方法,可在不同情况下重复使用。
    例如,当用户在文本编辑器中键入时,我需要减慢调用顺序时,我就使用它

    但我确信,在遍历集合时,它也应该起作用。在这种情况下,它可以对请求进行排队,并可以发送一个AJAX调用,而不是12个

    queueing = {
        callTimeout:                 undefined,
        callTimeoutDelayTime:        1000,
        callTimeoutMaxQueueSize:     12,
        callTimeoutCurrentQueueSize: 0,
    
        queueCall: function (theCall) {
            clearTimeout(this.callTimeout);
    
            if (this.callTimeoutCurrentQueueSize >= this.callTimeoutMaxQueueSize) {
                theCall();
                this.callTimeoutCurrentQueueSize = 0;
            } else {
                var _self = this;
    
                this.callTimeout = setTimeout(function () {
                    theCall();
                    _self.callTimeoutCurrentQueueSize = 0;
                }, this.callTimeoutDelayTime);
            }
    
            this.callTimeoutCurrentQueueSize++;
        }
    }
    

    如果一个用户发送多个请求,旧的complete不会被覆盖吗?@dredrik-No,javascript在基于函数的范围内处理变量
    oldComplete
    对于
    $.ajaxQueue()
    的每次调用都是不同的,如果ajax url依赖于上一次ajax调用的返回数据,该怎么办?有什么办法让它工作吗?这对我来说太好了。有一个问题@gnarf,假设abort方法是从队列中删除ajax请求,我是否正确?如果是的话,怎么称呼?我尝试了$.ajaxQueue.abort();有没有办法知道队列已完全清空/完成并触发一个操作?时间和潮流继续前进(正如@gnarf回来指出的)。。。从1.5开始,jQuery有一系列的
    延迟的
    对象,包括
    when()
    ,这些对象非常适合这种情况。见:和
    function ajaxPromise(elInfo) {
        return new Promise(function (resolve, reject) {
            //Do anything as desired with the elInfo passed as parameter
    
            $.ajax({
                type: "POST",
                url: '/someurl/',
                data: {data: "somedata" + elInfo},
                success: function (data) {
                    //Do anything as desired with the data received from the server,
                    //and then resolve the promise
                    resolve();
                },
                error: function (err) {
                    reject(err);
                },
                async: true
            });
    
        });
    }
    
    function callAjaxSynchronous(elCollection) {
        if (elCollection.length > 0) {
            var el = elCollection.shift();
            ajaxPromise(el)
            .then(function () {
                callAjaxSynchronous(elCollection);
            })
            .catch(function (err) {
                //Abort further ajax calls/continue with the rest
                //callAjaxSynchronous(elCollection);
            });
        }
        else {
            return false;
        }
    }
    
    queueing = {
        callTimeout:                 undefined,
        callTimeoutDelayTime:        1000,
        callTimeoutMaxQueueSize:     12,
        callTimeoutCurrentQueueSize: 0,
    
        queueCall: function (theCall) {
            clearTimeout(this.callTimeout);
    
            if (this.callTimeoutCurrentQueueSize >= this.callTimeoutMaxQueueSize) {
                theCall();
                this.callTimeoutCurrentQueueSize = 0;
            } else {
                var _self = this;
    
                this.callTimeout = setTimeout(function () {
                    theCall();
                    _self.callTimeoutCurrentQueueSize = 0;
                }, this.callTimeoutDelayTime);
            }
    
            this.callTimeoutCurrentQueueSize++;
        }
    }