代码之家  ›  专栏  ›  技术社区  ›  Scott Evernden

排序ajax请求

  •  61
  • Scott Evernden  · 技术社区  · 16 年前

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

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

    9 回复  |  直到 16 年前
        1
  •  114
  •   Artem P    12 年前

    jQuery 1.5版+

    我开发了一个 $.ajaxQueue() $.Deferred , .queue() ,和 $.ajax() promise 在请求完成时解决。

    /*
    * 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);
    

    查询1.4

    如果您使用的是jquery1.4,那么可以利用空对象上的动画队列为元素的ajax请求创建自己的“队列”。

    $.ajax() 更换。此插件 使用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);
    

    示例用法

    <ul id="items"> 其中有一些 <li> 我们想要复制的(使用ajax!)到 <ul id="output">

    // get each item we want to copy
    $("#items li").each(function(idx) {
    
        // queue up an ajax request
        $.ajaxQueue({
            url: '/echo/html/',
            data: {html : "["+idx+"] "+$(this).html()},
            type: 'POST',
            success: function(data) {
                // Write to #output
                $("#output").append($("<li>", { html: data }));
            }
        });
    });
    

    jsfiddle demonstration - 1.4 version

        2
  •  13
  •   Thomas Nadin    12 年前

    $.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');
    });
    

    看到了吗 the example

        3
  •  3
  •   naikus    16 年前

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

    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
         }
      };
    
        4
  •  3
  •   DonnieKun    16 年前

    让我试着用闭包。。。

    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);
    
        5
  •  2
  •   BishopZ    14 年前

    是的,虽然其他的答案会奏效,但它们都是大量的代码和凌乱的外观。js的设计就是为了优雅地解决这个问题。 https://github.com/bishopZ/Frame.js

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

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

    但这不会:

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

    另外,使用Frame可以瀑布式地处理响应对象,并在整个AJAX请求系列完成后处理它们(如果您愿意):

    var listOfAjaxObjects = [ {}, {}, ... ]; // an array of objects for $.ajax
    $.each(listOfAjaxObjects, function(i, item){
        Frame(function(nextFrame){ 
            item.complete = function(response){
                // do stuff with this response or wait until end
                nextFrame(response); // ajax response objects will waterfall to the next Frame()
            $.ajax(item);
        });
    });
    Frame(function(callback){ // runs after all the AJAX requests have returned
        var ajaxResponses = [];
        $.each(arguments, function(i, arg){
            if(i!==0){ // the first argument is always the callback function
                ajaxResponses.push(arg);
            }
        });
        // do stuff with the responses from your AJAX requests
        // if an AJAX request returned an error, the error object will be present in place of the response object
        callback();
    });
    Frame.start()
    
        6
  •  2
  •   Sandip Ghosh    8 年前

    我贴出这个答案是想它可能会帮助其他人在未来,寻找一些简单的解决方案在同一个场景。

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

    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;
        }
    }
    
        7
  •  1
  •   unomi    16 年前

    http://developer.yahoo.com/yui/3/io/#queue 以获得该功能。

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

        8
  •  1
  •   Shanimal    10 年前

    then .

    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);
    }
    

    http://plnkr.co/edit/meHQHU48zLTZZHMCtIHm?p=preview

        9
  •  1
  •   Oleg Lazaryev    7 年前

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

    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++;
        }
    }