1

If I am doing an async call like the following, how can chain them with promises, so i can do stuff in order? In this example, what ends up happening is that arr will push the items out of order. I'd prefer an answer with promises, but anything will do as long as it works

var fbArrOfAlbumNames = ['Profile Pictures', 'Cover Photos', 'Mobile Uploads'];
var arr = [];
for(var x = 0; x < fbArrOfAlbumNames.length; x++) {
  (function(cntr) {
    FB.api('/' + fbArrOfAlbumNames[cntr] + '/photos/', {fields: 'picture,album'}, function(response) {
      arr.push(response);
    }
  })(x);
}
D-Marc
  • 2,937
  • 5
  • 20
  • 28
  • 1
    Do you mean for this code to be `ajaxCallFbAPI(someArgs, function(err, data) {...});` What you show in your question does not look like a typical asynchronous API. – jfriend00 Mar 30 '17 at 23:35
  • Chaining asynchronous code to run synchronously kind of defeats the purpose of it. First thing I would try is to use [Promise.all()](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) and add your responses into your array when they're all done, this will at least allow it to only run as slow as the slowest call, not the cumulative time of all of them. – aaronw Mar 30 '17 at 23:41
  • @jfriend00 Yes you're right. I was being terse purposefully because it doesn't matter the specifics of it. Could be any async call. – D-Marc Mar 30 '17 at 23:47
  • I had the same idea as @jfriend00, his example is really good. – aaronw Mar 30 '17 at 23:48
  • 1
    Well, the specifics do matter. Why don't you fix your example in your question to at least be a legal async call so people can help you more specifically (you can use the "edit" link to fix it). In general, you get better, more pertinent and more detailed answers when you show your ACTUAL code, not some make believe code example. Often the devil is in the details and we can only be thorough when we can see your actual code. Plus, we are more likely to be able to offer you an answer you didn't even know to ask about when we see the real issue and real code. – jfriend00 Mar 31 '17 at 00:03
  • _"so i can do stuff in order"_ Do you mean call functions in sequential order, return results in sequential order, or both? – guest271314 Mar 31 '17 at 00:05
  • Updated the question to make it clearer. Though the "stuff in order" part was already specified before. I'm trying to push the response to an array in order. – D-Marc Mar 31 '17 at 00:09
  • `Promise.all()` returns resulting array in same order as iterable passed as parameter. You could also schedule call to same function until array `.length` is `0` to iterate array – guest271314 Mar 31 '17 at 00:11

1 Answers1

8

Assuming your ajax calls can actually be run in parallel and you just want the results in order, then you can promisify the ajax function and use Promise.all() to get all the results in order:

// promisify the ajax call
function fbAPIPromise(path, args) {
    return new Promise(function(resolve, reject) {
        FB.api(path, args, function(results) {
            if (!result) return resolve(null);
            if (result.error) return reject(result.error);
            resolve(result);
        });
    });
}

var promises = [];
for (var x = 0; x < 10; x++) {
     promises.push(fbAPIPromise('/' + fbArrOfAlbumNames[x] + '/photos/', {fields: 'picture,album'});
}
Promise.all(promises).then(function(results) {
     // results is an array of results in original order
}).catch(function(err) {
     // an error occurred
});
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 1
    Updated code example to use your actual `FB.api()` call since you added that to your question. – jfriend00 Mar 31 '17 at 00:23
  • I now understand what @guest271314 was saying earlier. If I try to set my values inside `fbAPIPromise()`, like `fbPicArr.thumb.push(response.data[0].picture)`, then it will still remain out of order. I suppose I could just traverse it after `Promise.all`, but I was wondering if there's a way to do it all inside `fbAPIPromise()` – D-Marc Mar 31 '17 at 11:18
  • @lazyboy78 - First you decide if it's possible to run your operations in parallel because that will get you a faster end result. If you are doing them in parallel, then each individual operation will finish in an indeterminate order relative to the rest of them. There are ways to still process them in order there (but not using `.push()`), but frankly it's just easier to traverse the final results that are already in order. That would be the recommendation. `results.forEach()` will let you process them in order. – jfriend00 Mar 31 '17 at 15:03
  • @lazyboy78 - You can also do `promises.push(fbAPIPromise(...).then(...))` and inside your `.then()` handler process the results and return the result for that item and then that's what will be in the `results` array at the end (already processed). – jfriend00 Mar 31 '17 at 15:05
  • Ya traversing the final results seems like the best solution. Was just wondering out of curiosity if there was an alternative as well. Excellent tips and I feel a lot more knowledgeable about using loops with promises – D-Marc Mar 31 '17 at 15:12