Noel Broda Noel Broda - 4 months ago 12
Javascript Question

How to know when all Promises are Resolved in a dynamic "iterable" parameter?

My problem is that I don't know how to know when a dynamic promise array has all the promises resolved.

Here an example:

var promiseArray = [];
promiseArray.push(new Promise(){/*blablabla*/});
promiseArray.push(new Promise(){/*blablabla*/});
Promise.all(promiseArray).then(function(){
// This will be executen when those 2 promises are solved.
});
promiseArray.push(new Promise(){/*blablabla*/});


I have a problem here. The
Promise.all
behavior will be executed when the previous 2 promises are solved, BUT, before those 2 promises were solved, a third promise where added and this new one won't be take in account.

So, what I need, is say something like: "Hey
Promise.all
, you have a dynamic array to check". How can I do it?

Remember that this is just an example. I know I can move the line
Promise.all
to the last line, but actually the new promises are added dynamically when another promises are solved, and the new promises could add new promises as well, so, it's a really dynamic array.

The real use case that I have is something like this:


  1. I use Twitter API to check if there are new Tweets (using the Search Api).

  2. In case I found new Tweets, I add it to a MongoDB (here we have Promises).

  3. In case that those new Tweets are related to a user that I do not have in my MongoDB (here we have new promises because I have to go to MongoDB to check if I have that user), we go to Twitter API to get user info (more promise) and we add those new users to MongoDB (yes, more promises).

  4. Then, I go to MongoDB to insert new values to associate the new tweets with those new users (more promises! wiii!).

  5. When all the queries to MongoDB are Resolved (all the selects, updates, inserts), close the MongoDB connection.



Another hard example:

var allPromises = [];

allPromises.push(new Promise(function(done, fail){
mongoDB.connect(function(error){
//Because mongoDB works with callbacks instead of promises
if(error)
fail();
else
ajax.get('/whatever').then(function(){
if (somethingHappens) {
allPromises.push(new Promise(function(done, fail){ //This promise never will be take in account
// bla bla bla
if (somethingHappens) {
allPromises.push(new Promise(function(done, fail){ //This promise never will be take in account
// bla bla bla
}));
} else {
ajax.get('/whatever/2').then(function(){
if (somethingHappens) {
allPromises.push(new Promise(function(done, fail){ //This promise never will be take in account
// bla bla bla
}));
}
});
}
}));
} else {
ajax.get('/whatever/2').then(function(){
if (somethingHappens) {
allPromises.push(new Promise(function(done, fail){ //This promise never will be take in account
// bla bla bla
if (somethingHappens) {
allPromises.push(new Promise(function(done, fail){ //This promise never will be take in account
// bla bla bla
}));
} else {
ajax.get('/whatever/2').then(function(){
if (somethingHappens) {
allPromises.push(new Promise(function(done, fail){ //This promise never will be take in account
// bla bla bla
}));
}
});
}
}));
}
});
}
});
});
}));

Promise.all(allPromises).then(function(){
// Soooo, all work is done!
mongodb.close()!
});


So, now, a beauty example. We need to call the
showAllTheInformation
function when the last (we don't know which is the last) promise is called. How do you do it?:

var name = 'anonimus';
var date = 'we do not know';

function userClikOnLogIn() {
$http.get('/login/user/password').then(function(data){
if (data.logguedOk) {
$http.get('/checkIfIsAdmin').then(function(data){
if (data.yesHeIsAnAdmin) {
$http.get('/getTheNameOfTheUser').then(function(data){
if(data.userHasName) {
$http.get('/getCurrentDate').then(function(data){
currentDate = data.theNewCurrentDate;
});
}
});
}
});
}
});
}

function showAllTheInformation() {
alert('Hi ' + name + ' today is:' + date);
}


here another example with more context:
https://jsfiddle.net/f0a1s79o/2/

Answer

There's no way out. You have to put all the promises in the array before calling Promise.all in it. In the example you presented, that's as simple as moving the last line to the top.

In case you are asynchronously filling the array, you should get a promise for that array, and use .then(Promise.all.bind(Promise)). If you don't know when you stop adding promises, this is impossible anyway as they might never all be resolved at all.


Regarding your "beauty example", you will want to learn about the magic of chaining. As I previosly said in the comments, you have to return a promise from every function in which you are doing anything asynchronous. Indeed, just add the missing returns:

function userClikOnLogIn() {
    return $http.get('/login/user/password').then(function(data){
//  ^^^^^^
        if (data.logguedOk) {
            return $http.get('/checkIfIsAdmin').then(function(data){
//          ^^^^^^
                if (data.yesHeIsAnAdmin) {
                    return $http.get('/getTheNameOfTheUser').then(function(data){
//                  ^^^^^^
                        if(data.userHasName) {
                            return $http.get('/getCurrentDate').then(function(data){
//                          ^^^^^^
                                currentDate = data.theNewCurrentDate;
                            });
                        }
                    });
                }
            });
        }
    });
}

userClikOnLogIn().then(function showAllTheInformation() {
//               ^^^^^ now you can chain onto it!
    alert('Hi ' + name + ' today is:' + date);
});

There is no array of promises here that dynamically grows, it's just that every function is returning a promise for the (asynchronous) result of the things it does.