Paul
Paul

Reputation: 326

jquery post call function n times but wait for each to complete before calling next one

I have a list things that I'm sending to PHP one at a time via $.post. I want to wait for each to complete before calling the next. I want to do this with JS not doing the looping with PHP as I want the return value from each to display.

var list = ["a", "b", "c"];
for (i = 0; i < list.length; i++) {
  $.post(con, {
    callScript: list[i],
  }, function(data, status) {
    //Do stuff here with the data on success

  });
}

I have looked at $.when but just can't sort out how to use it. Ever example assumes that there is a set number of functions and not the same function n times. I also know that async false is not allowed.

Is there a way to get that to run?

Upvotes: 3

Views: 201

Answers (3)

Graham
Graham

Reputation: 7802

There is a VERY widely used library called "async" that makes this sort of thing very easy. Here are the docs for async series which is probably what you want to do here.

Here is the example from the docs:

async.series([
    function(callback) {
        // do some stuff ...
        callback(null, 'one');
    },
    function(callback) {
        // do some more stuff ...
        callback(null, 'two');
    }
],
// optional callback
function(err, results) {
    // results is now equal to ['one', 'two']
});

The pattern for the callback is pass an error as the first parameter if something went wrong, otherwise pass a null as the first parameter then the actual return value as the second parameter. That pattern is used all over asynchronous JavaScript on the server and in the browser.

That final function is what gets executed once the series is complete.

You can also get tricky with this knowing that variables can be functions in JavaScript and build your queue like this:

var processMe = [];

processMe.push(callScript('two'));
processMe.push(callScript('four'));
processMe.push(callScript('six'));

async.series([ processMe ],
function(err, results) {
    // results is now equal to ['two', 'four', 'six']
});

function callScript(value, callback) {
  // do something here
  callback(null, value);
}

You can also use waterfall if you need to pass results from one step to another.

If it truly is the same code multiple times, use the times operator to simply iterate the same function N times:

// Pretend this is some complicated async factory
var createUser = function(id, callback) {
    callback(null, {
        id: 'user' + id
    });
};

// generate 5 users
async.times(5, function(n, next) {
    createUser(n, function(err, user) {
        next(err, user);
    });
}, function(err, users) {
    // we should now have 5 users
});

Upvotes: 0

nem035
nem035

Reputation: 35491

Recursion is your good friend here. You can create a function that invokes itself for each item, calling the next one after the async operation of the current one is finished. The recursion stops when we run out of items.

function postInOrder(list, index = 0) {
  if (index < list.length) {
    $.post(con, {
      callScript: list[index],
    }, function success(data, status) {
      // do stuff here with the data on success
      // ...
      postInOrder(list, index + 1); // <-- run next script on completion
    });
  }
}

postInOrder(["a", "b", "c"])

Here's an example with a fake post method:

function postInOrder(list, index = 0) {
  if (index < list.length) {
    console.log(`Start: ${list[index]}`)
    post(function success() {
      console.log(`Finish: ${list[index]}`)
      postInOrder(list, index + 1); // <-- run next script on completion
    });
  }
}

postInOrder(["a", "b", "c"])

function post(cb) { setTimeout(cb, 1000); }

Upvotes: 3

Jonas Wilms
Jonas Wilms

Reputation: 138407

You may also reduce it to a promise queue:

list.reduce(function(prom,listEl){
  return prom.then(function(){
   return new Promise(function(next){
    $.post(con, {
      callScript: listEl,
    }, function(data, status) {
      //Do stuff here with the data on success
      next();
    });
  });
 });
},Promise.resolve());

(I think wrapping into a promise is not neccessary, may someone whos shure about jquerys syntax feels free to edit ;))

Upvotes: 3

Related Questions