How to find out when all Promises are allowed in a dynamic "iterable" parameter?

My problem is that I don’t know how to know when the dynamic promises grid resolves all the promises.

Here is 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. The behavior of Promise.all will be executed when the previous 2 promises are resolved, BUT, before these 2 promises are resolved, the third promise is added, and this new one will not be taken into account.

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

Remember, this is just an example. I know that I can move the Promise.all line to the last line, but in fact new promises are added dynamically when other promises are resolved, and new promises can also add new promises, so this is really a dynamic array.

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

  1. I use the Twitter API to check for new tweets (using Search Api).
  2. In case I found new tweets, I add them to MongoDB (here we have promises).
  3. In case these new tweets are associated with 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 this user), we go to the Twitter API for user information (more promises), and we add new users to MongoDB (yes, more promises).
  4. Then I go to MongoDB to insert new values ​​to associate new tweets with these new users (more promises! Wiii!).
  5. When all requests in MongoDB are allowed (all selections, updates, inserts), close the MongoDB connection.

Another tough 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, an example of beauty. We need to call the showAllTheInformation function when showAllTheInformation last (we don't know which last) promise. How do you do this?:

 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's another example with great context: https://jsfiddle.net/f0a1s79o/2/

+8
javascript promise ecmascript-6
source share
4 answers

No exit. You must put all the promises in an array before calling Promise.all in it. In the example you presented, it is just like moving the last line up.

If you fill the array asynchronously, 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 not possible anyway, as they may never be allowed at all.


As for your "example of beauty," you need to learn about chain magic. As I said in the comments, you should return to give a promise from every function in which you do something asynchronous. In fact, just add the missing return s:

 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 promises array that grows dynamically, namely that each function returns a promise (asynchronous) result of what it does.

+2
source share

You can make a neat recursive function to transfer Promise.all to handle additions to the original promise:

 /** * Returns a Promise that resolves to an array of inputs, like Promise.all. * * If additional unresolved promises are added to the passed-in iterable or * array, the returned Promise will additionally wait for those, as long as * they are added before the final promise in the iterable can resolve. */ function iterablePromise(iterable) { return Promise.all(iterable).then(function(resolvedIterable) { if (iterable.length != resolvedIterable.length) { // The list of promises or values changed. Return a new Promise. // The original promise won't resolve until the new one does. return iterablePromise(iterable); } // The list of promises or values stayed the same. // Return results immediately. return resolvedIterable; }); } /* Test harness below */ function timeoutPromise(string, timeoutMs) { console.log("Promise created: " + string + " - " + timeoutMs + "ms"); return new Promise(function(resolve, reject) { window.setTimeout(function() { console.log("Promise resolved: " + string + " - " + timeoutMs + "ms"); resolve(); }, timeoutMs); }); } var list = [timeoutPromise('original', 1000)]; timeoutPromise('list adder', 200).then(function() { list.push(timeoutPromise('newly created promise', 2000)); }); iterablePromise(list).then(function() { console.log("All done!"); }); 

Keep in mind that this applies only to additions and that it is still a little dangerous: you need to make sure that the callback order is such that any promises in flight add themselves to the list before the Promises.all callback can be called.

+10
source share

If you can use promises or their use, as well as problems with the scope, then I think you could more easily approach the problem: how many promises stand out?

In other words, you do not need to keep track of all the promises, just read them.

 var outstanding = 0; var p1 = new Promise(){/*blablabla*/}; var p2 = new Promise(){/*blablabla*/}; ++outstanding; p1.then( (data) => { ... if (0 >= --outstanding) // All resolved! } // dynamic set of promises, so later we decide to add another: var p3 = new Promise(){/*blablabla*/}; ++outstanding; p3.then( ... ); // as above 

To improve the above, wrap it all in a meta promise (equivalent to the one that Promise.all will return for the static set of promises) ...

  // Create a promise that tracks completion of a dynamic set of instrumented promises. getCompletionP() { let rslv = null; const p = new Promise( function(resolve, reject) { rslv = resolve; } ); p.resolve = rslv; assert( p.resolve ); p.scheduled = 0; p.onSchedule = function() { ++this.scheduled; }; p.onComplete = function() { if (0 >= --this.scheduled) this.resolve(); }; return p; } 

Now call cp.onSchedule () before each then () call and cp.onComplete at the end of each then (), and cp will be resolved after all your promises, then the functions will be completed. (Of course, you will also need to process catch statements).

This will be resolved when all the code planned through Promise.then is completed, while the question will ask what will be resolved when all the promises are resolved. This can be achieved by adding calls after approving the promises permission instead, but this is not possible using third-party libraries, and I think they will be functionally the same.

This will not work for all cases, but since the accepted answer is that it (a dynamic set of promises) cannot be executed, I believe that it can be useful, although it has become more complicated (messy) since I wrote this !

+1
source share

@JeffBowman and @Bergi have the right idea: recursive wait and count promises. Here is my implementation in Coffeescript)

 Promise = require 'bluebird' class DynamicPromiseCollection promises = [] add:(p)-> promises.push p wait_for_all:-> # # Wait for all current promises, then check for new promises... # ...if there are new promises, then keep waiting ( recursively ). # # Resolve only when all promises are done, and there are no new promises. # make_promise = -> num_before = promises.length p = Promise.all(promises).then -> num_after = promises.length if num_after > num_before return make_promise() # recursive -- wait again else return true # all done now p = make_promise() return p # # let test this... # promises = new DynamicPromiseCollection() # # pretend to get remote data # get_remote_data = -> new Promise (resolve,reject)-> setTimeout -> resolve "data" ,500 # # get data, wait, then get more data... # promises.add get_remote_data().then (data)-> console.log "got " + data promises.add get_remote_data().then (data)-> console.log "got " + data # # this should wait for both data # promises.wait_for_all().then -> console.log "...and wait_for_all is done." 
0
source share

All Articles