Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make promise.all wait for nested promise.all?

So I have a problem with JavaScript Promises. I'm using native implementation for the sake of reducing dependencies.

An illustrative example of what I need.

I need to retrieve lists of books, book authors and purchases. I also need an author profile for each of the authors. After I got all of that, I need to create a nice set of Authors with their books and purchase list for each of the books.

Lists and profiles are separate API JSON calls. The only dependency is that I need a list of authors to be able to get author profiles.

I've solved this with Promises. I use Promise.all to get 3 API JSON requests for: authors, books and purchases. I use yet another Promise.all to get all the profiles for each of the authors I get (I loop through the list, map urls for each profile and send a batch of requests in parallel).

I run the profile request batch as soon as I get the list of authors, thus in the "Then" handler of the author list promise.

Now, the problem:

To be sure that all promises, 3 lists and all profiles, will be done prior to my assembling of the library set, I would need to send a profile batch of requests when I'm done with all the lists, in the first Promise.all Then handler.

But: lists of books an purchases take much longer time than the list of authors and I don't want to wait for all of those to send a batch of profile requests, so I send it in the Then handler of the author-list promise so these start as soon as I have the info.

However, a nested Promise.all does not count towards its parent Promise.all Then handler so since my FinalFunction is in the Then of the top-level Promise.all, it may (and sometimes does) fire before the nested Promise.all has finished retrieving all author profiles.

I need to be able to start all of the Promise requests as soon as possible, but only the batch of author requests depends on one promise being complete to start, so I need to wait on that one. All other should start independently.

Pseudo code

Promise.all(
  requestBooks().then(){},
  requestAuthors().then(){
    GenerateArrayOfAuthorUris();
    // now send a promisifyed batch of per-author requests
    Promise.all(
      [array of author uris to run requests for]
    )
    .then(){
      // If I run this here, I may not have upper-level requests done
      runCalculationsPerAuthorForAllAuthorsBooksPurchasesReceived();
    }
  },
  requestPurchases().then(){},
)
.then(){
  // this will fire when 3 top requests are done, but won't wait for 
  //   the nested Promise.all with per-author requests
  runCalculationsPerAuthorForAllAuthorsBooksPurchasesReceived();
}

If I do it this way, I'm wasting precious time by waiting for requests I don't need to wait on just to start per-author requests:

Promise.all(
  requestBooks().then(){},
  requestAuthors().then(){
    GenerateArrayOfAuthorUris();
  },
  requestPurchases().then(){},
)
.then(){
    // now send a promisifyed batch of per-author requests
    Promise.all(
      [array of author uris to run requests for]
    )
    .then(){
      // If I run this here, I may not have upper-level requests done
      runCalculationsPerAuthorForAllAuthorsBooksPurchasesReceived();
    }
}

Hopefully this clarifies what I need.

Thank you.

This is the code sample: https://jsbin.com/qizohasofa/edit?js,console

like image 442
Anthony Avatar asked Apr 11 '16 09:04

Anthony


People also ask

Does promise all wait for all promises?

Using Promise.all()Promise.all waits for all fulfillments (or the first rejection).

Does promise all run promises in parallel?

As you can see, Promise. all executes code concurrently, but what is parallel execution? JavaScript is single-threaded and can only perform a single chunk of work at a time, so parallel execution is not possible with JavaScript, except for some circumstances such as web workers.

Can you await the same promise multiple times?

No. It is not safe to resolve/reject promise multiple times. It is basically a bug, that is hard to catch, becasue it can be not always reproducible.


1 Answers

As you were told in the comments, you didn't return anything from your functions, so then didn't know what inner promises to wait for.

function getJokeCategories() {
    return Promise.all([
//  ^^^^^^
        pgetJSON("http://api.icndb.com/categories"),
        pgetJSON("http://api.icndb.com/jokes/count").then(function(data) {
            var jokesToGet = [];
            for (var i=0; i<data; i++){
                jokesToGet.push("http://api.icndb.com/jokes/"+i);
            }
            return Promise.all(jokesToGet.map(function(jk) {
//          ^^^^^^
                return pgetJSON(jk).then(function(joke) {
//              ^^^^^^
                    console.log(jk + " just returned", joke);
                    return joke;
//                  ^^^^^^
                });
            })).then(function(jokes) {
                console.log("All jokes returned. This does happen only when all jokes are retrieved.");
                return {count:data, jokes:jokes};
//              ^^^^^^
            });
        })
    ]);
}
getJokeCategories().then(function(result) {
    console.log(result, "This does happen at the very end when joke count, joke categories and all jokes are returned.");
});
like image 59
Bergi Avatar answered Oct 25 '22 00:10

Bergi