Anto Jose
Anto Jose

Reputation: 35

How to send Javascript/jQuery AJAX POST requests sequentially, looping over an array of request data, using Promises?

I have a huge list of articles, visually indicated by a table listing of article titles, on which we have to perform a particular processing action, processing items one after the other, by sending a POST request with the id of that article, while removing that row from the table listing, as a visual indicator of the progress.

This is what I used originally:

var article_ids = [1,2,...];
article_ids.each(function (value, index) {
  var id = value;
  jQuery.ajax({
    type: "POST",
    url: "index.php?process_article",
    data: {article_id: id},
    cache: false,
    async: false
  })
  .done(function(reponse) {
    console.log(response);
    jQuery('.article-id-' + id).css('display', 'none');
  });  
});

And it works fine in FireFox, processing items one by one, removing corresponding rows from the table listing.

But in Google Chrome, all of these seem to be processed in one go, instead of these requests happening one after the completion of the other.

After a bit of googling, found references saying to use Javascript Promises, which I couldn't figure out how to use for this case.

And then, instead, I changed the above, to the following recursive function, which seems to work fine on both Chrome and Firefox, with requests happening one after the other:

var article_ids = [1,2,...];
function processIds(ids) {
  if (ids.length > 0) {
    id = ids.shift();     
    console.log("Processing ID: " + id);
    jQuery.ajax({
      type: "POST",
      url: "index.php?process_article",
      data: {article_id: id},
      cache: false
    })
    .done(function() {
      jQuery('.article-id-' + id).css('display', 'none');
      processIds(ids);
    });
  } else {
    alert("Successfully processed all articles.");
  }
}
processIds(article_ids);

DOUBTS:

  1. Is this a good way to go about this?
  2. How could we achieve the same using Javascript Promises?

Upvotes: 2

Views: 96

Answers (1)

Rajit
Rajit

Reputation: 808

The recursive solution you've suggested works and makes sense, IMO. In this situation I usually use Array.reduce, though.

Here's an example:

function processIds(ids) {
  return ids.reduce((promise, nextId) => {
    return promise.then(() =>
      jQuery.ajax({
        type: "POST",
        url: "index.php?process_article",
        data: { article_id: nextId },
        cache: false,
      })
    );
  }, Promise.resolve());
}

Note that the return values contained in the promises are thrown away in this example and only the very last value will be retained. It would be possible to modify this to capture and aggregate all return values if you desired.

It's also easy to extract this into something reusable. For example:

function forEachAsync(myArray, funcPromise) {
  return myArray.reduce((promise, nextValue) => {
    return promise.then(() => funcPromise(nextValue));
  }, Promise.resolve());
}

And then you could use your new forEachAsync function as follows:

function processIds(ids) {
  return forEachAsync(ids, processArticle);
}

function processArticle(id) {
  return jQuery.ajax({
    type: "POST",
    url: "index.php?process_article",
    data: { article_id: nextId },
    cache: false,
  });
}

I believe the latest versions of jQuery use promises that are compatible with JavaScript promises, but this is something you should check and be aware of.

I believe they were introduced in ES2015 and jQuery introduced compatibility in v3.0.

Upvotes: 2

Related Questions