dtgee
dtgee

Reputation: 1272

How to reorder execution of asynchronous functions?

if (option == 'Follow All') {
    for (var i = 0; i < userArray.length; i++) {
        followUser(params..);
    }

    // How to get this part to execute after followUser is done? (Basically when the for loop finishes)
    alert("There was a problem processing your request on Twitter to follow the following users: " + $('#errored-users').val());
    $('#errored-users').val('');
}

How can I call this first multiple times and wait for it to finish?

var followUser = function(params..) {
    $.post('/api/1.0/followUser.php', {
        'user_to_follow': username,
        'username': user
    }).done(function(data) { {
            if (!is_batch) {
                alert("There was a problem processing your request on Twitter to follow @" + username);
            } else {
                //This currently gets executed last?
                var names = $('#errored-users').val();
                if (names == "") {
                    names = '@' + username + ', ';
                } else {
                    names += '@' + username + ', ';
                }
                $('#errored-users').val(names);
            }
};

Upvotes: 1

Views: 106

Answers (4)

Jon
Jon

Reputation: 437336

Since you are already using jQuery, you can easily use the AJAX requests/promises and wait for all of them to complete. $.when can help you a lot with this:

var followUser = function(params..) {
    // return the promise!
    return $.post('/api/1.0/followUser.php', { ... });
};

if (option == 'Follow All') {
    var promises = [];
    for (var i = 0; i < userArray.length; i++) {
        promises.push(followUser(...));
    }

    $.when.apply(null, promises)
    .done(function() {
        // all users were successfully followed
    })
    .fail(function() {
        // at least one follow failed; no information about the others
        alert("There was a problem processing your request...");
        $('#errored-users').val('');
    });
}

This will call the .done handler when all requests have completed, but it will call the .fail handler as soon as just one has failed.

If instead you want some handler to run after all requests have completed (either success or failure) you 'd need to do it more manually, for example:

var followUser = function(params..) {
    // return the promise!
    return $.post('/api/1.0/followUser.php', { ... });
};

if (option == 'Follow All') {
    var outcomes = { done: [], failed: [] };
    var total = userArray.length;
    function allFinished() {
         return outcomes.done.length + outcomes.failed.length == total;
    }

    for (var i = 0; i < total; i++) {
        followUser(...)
        .done(function() {
            outcomes.done.push(username);
        })
        .fail(function() {
            outcomes.failed.push(username);
        })
        // this must come last
        .always(function() {
            if (allFinished) {
                // outcomes contains the results
            }
        })
    }
}

This will still use jQuery's notion of a request having succeeded or failed, which is based on Twitter's HTTP response codes. If you want to customize this behavior you can amend followUser as such:

var followUser = function(params..) {
    return $.post('/api/1.0/followUser.php', { ... })
        .then(
        // first argument handles HTTP response successes, but you can
        // convert them to failures here:
        function(data) {
            if (convertSuccessToFailure) {
                return $.Deferred.reject(data);
            }
        });
};

Upvotes: 1

GordyD
GordyD

Reputation: 5103

A potential solution for this is to use Promises (see here for an in-depth explanation). It provides a new style of coding in Javascript that effectively enables you to make asynchronous code synchronous. (This is a big simplification of Promises - see here for an article explaining it a little bit more).

There are various implementations which you could use. The one I most use is found here: https://github.com/cujojs/when. The examples provided within it's wiki demonstrates the power of promises (see here).

The basic outline for your code using when.js would look and read something like this:

if (option == 'Follow All') {
  var deferreds = [];
  for (var i = 0; i < userArray.length; i++) {
      deferreds.push(followUser(params..));
  }

  when.all(deferreds).then(function everythingWasFine(suceededUsernames) {
    //do something with the responses e.g.
    alert(succeededUsernames.length + ' users were followed');
  },
  function somethingWentWrong(failedUsernames) {
    alert("There was a problem processing your request on Twitter to follow the following users: " + failedUsernames.join(','));
  });
}

var followUser = function(params..) {
  var defer = when.defer();
  $.post('/api/1.0/followUser.php', {
    'user_to_follow': username,
    'username': user
  }).done(function(data) {
        if (failure) {
           defer.reject(username);
        } else {
           defer.resolve(username);
        }
   });
   return when.promise;
 };

Upvotes: 0

Lorenz Meyer
Lorenz Meyer

Reputation: 19895

You could define a global variable which holds the number of calls to followUser:

if (option == 'Follow All') { var countUsers = userArray.length; for (var i = 0; i < countUsers; i++) { followUser(params..); } }

Then you change the anonymous function to count backwards and execute your last statement if all users are done:

function(data) { if (!is_batch) { alert("There was a problem processing your request on Twitter to follow @" + username); } else { (...) } countUsers--; if(countUsers == 0){ alert("There was a problem processing your request on Twitter to follow the following users: " + $('#errored-users').val()); $('#errored-users').val(''); } };

Upvotes: 0

Sean Vieira
Sean Vieira

Reputation: 159865

As of jQuery 1.5 any of the $.ajax family of functions return a promise - and you can combine multiple promises into a new promise that will be resolved when all the child promises are resolved using $.when:

function followUser(/* params */) {
    return $.post('/api/1.0/followUser.php', {
            user_to_follow: username,
            username: user
        });
}

var userRequests = [];
for (var i = 0, l = userArray.length; i < l; i++) {
    userRequests.push(followUser(/* params */));
}
$.when.apply($, userRequests).then(function(data) { /* etc. */ });

Upvotes: 0

Related Questions