Search code examples
javascriptjqueryasynchronousjquery-callback

jQuery JavaScript Nested Asynchronous Functions callback


I'm a little confused how to determine when async function called multiple times from another one is finished a call from the last iteration:

function MainAsyncFunction(callback) {
  for (var i = 0; i < 10; i++) {
    SubAsyncFunction(function(success) {
      if (i >= 10 && success) { // THIS IS WRONG?!
        callback(true); // happens too early
      }
    });
  }
};

function SubAsyncFunction(callback) {
  SubSubAsyncFunction(function() {
        callback(true);
  });
}

What I'm doing is calling the Google Distance Matrix service, which has a limitation of 25 destinations, hence I'm having to split my array of destinations to call this service multiple times but I don't understand when it's finished.

and in the main bit of code I can tell that the second iteration of the loop in the MainAsyncFunction hasn't yet completed when it does a call back.

I think my problem is I haven't got my head around the order of events when dealing with Async functions in JavaScript... please explain how the subject is normally achieved.


Solution

  • You could use the jQuery Deferred object, which acts as a token representing the status of an async operation.

    The following is a simplified example:

    //set up your sub method so that it returns a Deferred object
    function doSomethingAsync() {
        var token = $.Deferred();
        myAsyncMethodThatTakesACallback(function() {
            //resolve the token once the async operation is complete
            token.resolve();
        });
        return token.promise();
    };
    
    //then keep a record of the tokens from the main function
    function doSomethingAfterAllSubTasks() {
        var tokens = [];
        for (var i=0; i < 100; i++) {
            //store all the returned tokens
            tokens.push(doSomethingAsync());
        }
    
        $.when.apply($,tokens)
            .then(function() {
                //once ALL the sub operations are completed, this callback will be invoked
                alert("all async calls completed");
            });
    };
    

    The following is an updated version of the OP's updated code:

    function MainAsyncFunction(callback) {
      var subFunctionTokens = [];
      for (var i = 0; i < 10; i++) {
        subFunctionTokens.push(SubAsyncFunction());
      }
    
      $.when.apply($,subFunctionTokens)
      .then(function() {
        callback(true);
      });
    };
    
    function SubAsyncFunction() {
      var token = $.Deferred();
      SubSubAsyncFunction(function() {
            token.resolve();
      });
      return token.promise();
    };​