简体   繁体   中英

Multiple AJAX success in order

I am loading a collection of blocks of data using AJAX. Each one of these blocks need to be rendered in the DOM in order, so I am requesting the first block and rendering it, then request the following and so on until the last one. When the last one is done, a deferred object is resolved to signal another function all blocks were loaded:

function loadBlock(block, limit) {
  var deferred = $.Deferred();
  (function request(block, limit) {
    $.ajax({
      url: 'block/' + block,
      success: function(html) {
        $('#block-' + block).html(html);
        var nextBlock = block + 1;
        if (nextBlock <= limit)
          request(nextBlock, limit);
        else
          deferred.resolve();
      }
    });
  })(block, limit);

  return deferred.promise();
}

However, this whole wait for the previous request to be completed can slow the process down a lot.

So now what I am trying to do is just fire all requests as once and let the server take as many as it can, but instead of loading each block after its corresponding request is done, I would want a block to wait until the previous one has been loaded.

Example: 10 requests are sent. We would want to render the blocks in order: 1, 2, 3, ... Let's say that the requests for the first three blocks are completed in order, so the blocks are loaded in the page as we want, but then block 5 is ready before block 4, we'd want to keep the block 5 somewhere and show it in the page only after block 4 is there, that is, after the request for block 4 is done and it has been rendered.

My questions then are:

  • How can I run in parallel some functions firing the requests and other functions loading the block in the page?
  • How can I signal the function loading a particular block that the previous one is done, so it can go ahead?

Also, I would need to keep the deferred logic because I still have to signal when the entire process has completed.

The most elegant way of doing so is to use Promise.all()

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

A more rudimentary way would be to mark an int to store the completed requests or an array of Booleans which is checked after each AJAX request is complete.

You can do it as follows:

var loadBlockOneDef = loadBlock(block, limit);
var loadBlockTwoDef = loadBlock(block, limit);
...
Promise.all([loadBlockOneDef, loadBlockTwoDef, ..])
    .then(function (values){ 
        //DO STUFF
    }

The parameter for Promise.all() is an array of promises / deferreds. The values is an array of each promise in the array, values[0] --> resolve-value of loadBlockOneDef, ...

this ensures the immediate execution of the ajax-request but resolving in order. It always waits for the previous Deferred to be resolved, before resolving the current one (even if the current ajax-call completes before the previous one).

var loadBlock = (function(previous){
    //the purpose of `removePreviousResponse` is to clean up 
    //the arguments returned by the Deferred
    //and to stay consistent with the behaviour of $.ajax()
    function cleanupArguments(previousArgs, currentArgs){
        //here `$.when()` is resolved with two arguments, 
        //the args for the previous and for the current ajax call
        //we want only the args for the current ajax-call to be further propagated

        //since we can not return multiple values, we wrap them into a Deferred 
        //that is resolved with multiple arguments
        var deferred = $.Deferred();
        deferred.resolve.apply(deferred, currentArgs);
        return deferred.promise();
    }

    return function(block){
        //resolves when _both_ promises are done,
        //the previous one and the current
        var current = $.when(previous, $.get('block/' + block))
            .then( cleanupArguments )
            .done(function(html, success, evt){
                //add html to the DOM
                $('#block-' + block).html(html);
            });

        previous = current;
        return current;
    }
})(null);

//the blocks are added in that order that you call `loadBlock()`
loadBlock(0);
loadBlock(1);
loadBlock(3).then(function(html, success, evt){
    //you can even append further logic
    console.log("block 3 has been loaded");
});
loadBlock(2);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM