I'm looking at promises-based code that does something like this:
var promises = [];
function eventHandler(e)
{
promises.push(getSomeData(e.opts));
Promise.all(promises)
.then(...)
.then(function()
{
promises = [];
});
}
Shouldn't the Promise.all based promise execute as many times as the eventHandler is called?
EDIT: what's happening here is the event handler gets called a few times and the previous promises are not finished yet. I need a way to be able to extend the list of promises and have only one final then run.
Update, OP has clarified they'd like to wait for all promises added before the original ones resolved as well -.all does not do this. Instead you can use thenable chaining.
var p = Promise.resolve([]); // create empty array promise, `when` in dojo
function eventHandler(e){
p = p.then(function(value){ // chain off the last value
return getSomeData(e.opts).then(function(single){
return value.push(single); // add to the array
});
});
}
This will add an item to the array each time the eventHandler is called - every time you .then it you will get the current values and not the last ones - but you can .then it as many times as you'd like. You can for example chain on when no events happened since the chaining time by:
var p2 == p;
p.then(function(results){
if(p !== p2) return p; // wait in case more events happened, but no more
return results;
});
Or wait for as long as you'd like:
function noMoreAddedInterim(){
var p2 = p;
return p.then(function(results){
if(p2 !== p) return noMoreAddedInterim(); // changed
else return results; // no more added
});
}
You have a race condition, you're calling Promise.all and only cleaning the array later so many promises are in the results twice or more.
Instead - put promises in a temporary variable, .all that and clear promises before the .then. Since you're adding promises one at a time you probably don't even need the .all here.
Also, you should attach a .catch handler to not miss any errors.
Related
This question already has answers here:
How to Wait for Multiple Promises for All Data Success Callbacks
(3 answers)
Closed 5 years ago.
This is my requirement with AngularJS:
for (var i = 0, len = self.Scope.data.length; i < len; i++)
{
var data = self.Scope.data[i];
var self = this;
//First asynchronous function
self.EcritureService.createNewData(data).then(() => {
})
//Second asynchronous function
self.OperationService.getOperation(data.idOperation).then((operation) => {
})
//Third asynchronous function
self.AccountService.getAccount(data.codeCompte).then((compte) => {
currentAccount = compte;
currentAccount.montant = currentAccount.montant+data.montant;
})
//Fourth one relies on the third result, So it must be executed sequentially
self.AccountService.updateAccount(currentAccount).then(() => {
})
}
// After all fetch loop's promises are resolved I want to execute some instructions here for to update the operation retrieved in the second function.
I want this loop iterator wait till all promises are resolved before going on the next step than make sure that all works are done, moving to last functionality that reside outside the loop bloc
Create arrays of promises and use $q.all(promise_array) to run when all promises are resolved
// map array of `$q.all()` promises
let promises = self.Scope.data.map((data) => {
//First asynchronous function
let promise_1 = self.EcritureService.createNewData(data).then(() => {})
//Second asynchronous function
let promise_2 = self.OperationService.getOperation(data.idOperation).then((operation) => {
})
//Third asynchronous function
let promise_3 = self.AccountService.getAccount(data.codeCompte).then((compte) => {
currentAccount = compte;
currentAccount.montant = currentAccount.montant + data.montant;
return currentAccount;
}).then((currentAccount) => {
//return promise of 4th inside `then()` of third
return self.AccountService.updateAccount(currentAccount).then(() => {});
});
// this `$q.all()` resolves when this mapped instance of above all resolve
return $q.all([promise_1, promise_2, promise_3]);
});
// resolves when all the loop instances of `$q.all()` resolve
$q.all(promises).then(()=>{
// run completion code here
}).catch(err=>{
console.log('Something failed in promise chain')
})
First off, I would say that you probably don't want those services to be promises, given that you are trying to get around the "promiseness" of them. So the simplest solution would be to rewrite the services to just return normally instead of via a promise.
But to answer your questions. Second part first - the easiest way to link the 4th promise to the 3rd is just to put it inside the 3rd .then, like this:
//Third asynchronous function
self.AccountService.getAccount(data.codeCompte).then((compte) => {
currentAccount = compte;
currentAccount.montant = currentAccount.montant+data.montant;
//Fourth one relies on the third result, So it must be executed sequentially
self.AccountService.updateAccount(currentAccount).then(() => {
})
})
If you put any error handling in then you might decide to put the nested promise in a .finally clause instead.
As for getting the loop to wait, for loops really aren't designed to do that. The easiest way to achieve that would to write your own loop and use $q.all to group together the promises. You would need to keep track of a loop counter, which you increment in the .then of $q.all and use a recursive function that breaks out when the required number of loops is done.
I am using the Kriskowal Q library to handle promises.
I wrote the following function to wait until all promises have been resolved.
function multiplePromises(targetArray) {
var promises = {};
for (var i=0; i<targetArray.length; i++) {
var promise = singlePromise(); // any async. function
promises[i] = promise;
};
return Q.all(promises);
};
and I call it as follows:
multiplePromises(targetArray).then(
function(success){
console.log("success");
},
function(error){
console.log("error", error);
}
);
I was wondering however whether there is an order in which the promises are resolved (e.g. is it synchronous?). I.e. does the function wait to trigger the next promise i+1 until promise i is resolved? Or alternatively is it like with all other async. methods, that it actually fires all the single promises and just waits until they are all done?
If the second is the case, how would one rewrite this function to make sure that promise i+1 is ONLY triggered once promise i has been resolved?
Update: test
I did a test and put:
promises[i] = i;
to check whether it resolves sycnhronously and it seems the case. However, it could be just that my async function is fast enough to actually resolve it that quick. Does this seem right?
there are several ways to achieve what you want
Minimal change to your code would be
function multiplePromises(targetArray) {
var promises = [];
var p = Promise.resolve(); // I don't know the Q equivalent of this
for (var i=0; i<targetArray.length; i++) {
p = p.then(function() {
return singlePromise();
});
promises[i] = p;
};
return Q.all(promises);
};
The promises will be executed by Q in the order you declare them, but there's no way to ensure the return order will be the same. That is how async works.
If you want them to resolve in order, the only way I can think of is calling them one after the other instead of doing it in a batch.
This other response will provide more info and some solutions:
https://stackoverflow.com/a/36795097/7705625
Promises in theory could be resolved in order (and it could be easy to write an example in which they would) but you should never count on that.
The whole point of functions like Promise.all() (or Q.all() or Bluebird.all() etc.) is that if waits for all of the promises to get resolved, no matter what is the order of that, and as soon as they are all resolved then the promise that the Promise.all() itself returns gets resolved with an array of values that the original promises resolved to.
In that array you will get always the same order as the order of promises in the array that was an argument to Promise.all(). But the order in time in which the original promises were resolves is not known and is not relevant, as that will have no effect on the result of using Promise.all() whatsoever.
I have 2 promises that should settle before I return a response to the client.
I am using bluebird.
Promise
.settle([prmoise1(), promise2()])
.then(function(results){
results.forEach(function(result){
if(result.isFulfilled()){
console.log(result);
}
});
});
What is the resolution order of promises?
Is promise1() always resolved first in this case?
If not, how do we access the result associated with a promise()?
In your code, your two promise generated functions are called one after another and they will complete in whatever order their natural timing causes them to complete in. If promise2() is a very fast async operation, it could very well complete before promise1() does and thus is would resolve it's promise before the other one. Or, it could very well be vice versa. In other words, there is no guarantee as it depends entirely on the timing of each async operation. When running async operations this way, you are running them in parallel (both in flight at the same time).
The results array that Promise.settle() generates will always be in the originally specified order just like with Promise.all() so the first item in the Promise.settle(...).then(function(results) {}) results array will always belong to promise1() and the second to promise2().
Note: Promise.settle() appears to have been removed from the latest version of Bluebird. You can use .reflect() on each geneated promise to simulate this behavior as described here. In fact, in the newest version of Bluebird, you would do this:
Promise.all([promise1().reflect(), promise2().reflect()]).then(function(results){
results.forEach(function(result){
if(result.isFulfilled()){
console.log(result);
}
});
});
In the newest Bluebird, you can make your own Promise.settle() that works with an array of promises:
Promise.settle = function(array) {
var reflects = [];
if (array) {
reflects = array.map(function(item) {
// if it's a promise with .reflect(), call `.reflect()`
if (typeof item.then === "function" && typeof item.reflect === "function") {
return item.reflect();
} else {
// cast into a promise and call .reflect()
return Promise.resolve(item).reflect();
}
});
}
return Promise.all(reflects);
}
No, like in .all(), all actions are called simultaneously* and the promises resolve whenever.
However, the results array will always be in the order of the original array of promises, so results[0] will be the result of promise1().
* They're called one after another, but we don't wait for the first to finish to start the second
I an using Q.js, and have a series of functions that return promises chained together with then callbacks, because, I need them to execute in order as a sequence.
var promiseOne = one();
promiseOne.then(two).then(three).done();
I also have some promise that can happen at any time. I need to know when they have ALL resolved - including the last then call back in the chain of promises. I was trying to use Q.all.then but it will reach Q.all's then before some of these promises -
Q.all([promiseOne, anotherPromise]).then(function(results) {
// will hit this before chain above is done
});
in my jsfiddle example, I just have the one promise in Q.all, but in reality it is one of many. Is there any way to make this work that that I can know when all, including chained then's, are resolved?
jsfiddle: http://jsfiddle.net/98rq0bs6/1/
A promise does not know what was chained to it. However, calling .then() does return a new promise which will know what it was chained from. You will need to take this new promise, which represents the result of the chained actions, and wait for it - instead of promiseOne, which was only the first link in the chain.
var promiseOne = one();
var promiseChain = promiseOne.then(two).then(three);
Q.all([promiseChain, anotherPromise]).then(function(results) {
// ^^^^^^^^^^^^ wait for the chain
…
}).done();
var listOfItems = [1, 2, 3, 4, 5];
// Creating an empty initial promise that always resolves itself.
var promise = $q.all([]);
// Iterating list of items.
angular.forEach(listOfItems, function (item) {
promise = promise.then(function () {
return $timeout(function () {
console.log('Item executed: ' + item);
}, 2000);
});
});
promise.finally(function () {
console.log('Chain finished!');
});
Source, In this solution, "angular.forEach" make possible that each promise be executed in chain, in other words, be a promises´s sequence
Suppose I have the following Parse cloud code:
// assume myObj is a parse object
myObj.set("field1", "foo");
Parse.Promise.as().then(function()
myObj.save(myObj, {
success: function(savedObj) {
// A
return Parse.Promise.as();
},
error: function(myObj, error) {
// B
return Parse.Promise.as();
}
});
// C
// note that we can get here without any return statement being called
}).then(function() {
// D
});
(Now, i know it would be simpler to just use promises for the whole thing:
myObj.save().then(
...
...but there are some functions that don't return promises, so sometimes you have no choice but to mix Backbone-style success/error blocks with promises.)
My question:
What happens when C is reached? Does execution pause on this promise until one of those return statements is reached, and then execution reaches D? Does execution advance directly to D after reaching C, without waiting for a return statement? Is this an error?
In other words, is it possible for execution to happen in the order C, D, A/B? Or will it always be C, A/B, D? (Or, I suppose, if save finishes crazy fast, something like A/B, C, D?)
Your returns are from the inner functions. If it was up to me I'd promisify the save function itself. However, if you're convinced you don't want to do this you still have to return a Parse.Promise from the then if you want it to wait for anything.
The then function returns a new promise and resolves that promise (executes further thens) when its return value is resolved. If that's just a value it will not wait for anything - if it's a promise it will wait for it in turn to resolve.
Please look at how to promisify for further reading.
In your example - this would look something like:
myObj.set("field1", "foo");
Parse.Promise.as().then(function(){
var p = Parse.Promise();
myObj.save(myObj, {
success: p.resolve.bind(p), // on success, resolve the promise
error: p.reject.bind(p) // on failure, reject it
});
// any code here _will_ execute before the `then` is called.
return p; // return the promise so `then` will wait
}).then(function(savedObj) {
// this will always run after `myObj.save` completed
// you can add a catch handler for the error case
});
However, if we pay attention we can notice Parse's save method already returns a promise - Parse promisified it for us - so this code can be significantly reduced to:
myObj.set("field1", "foo");
Parse.Promise.as().then(function(){
return myObj.save(myObj)
}).then(function(savedObj) {
// this will always run after `myObj.save` completed
// you can add a catch handler for the error case
});
Which in turn can be reduced to:
myObj.set("field1", "foo");
myObj.save().then(function(savedObj){
// object save is done here
});
Answer seems to be that it won't wait for A or B -- it'll move on, probably with D executing before A or B.
In any case, it's basically a bug -- though not one the runtime will complain about -- to invoke asynchronous code (like I do with save()) but not explicitly return a local promise at the end of a then function block that would make the bigger promise wait until the asynchronous code finishes. In other words, I should do something like Benjamin Gruenbaum's answer suggests.
As a rule of thumb just keep returning promises, they will be automagically resolved. All Parse async methods return promises so just keep returning until you are done.
I wouldn't recommend to use the success and error methods. Using callbacks and promises together is not a good idea since you have to write a lot of extra code to account for it. If everything in your app return promises you can remove a block or add a another one very easily. Just create functions that return a promise.
Also, how you return promises while vary depending on the scope needed in each block.
So for example:
function myPromise (a) {
return Parse.Promise.as({name: 'David Bowie', a : a});
}
myObj.set("a", 1);
myObj.save(obj).then(function(obj) {
var a = obj.get('a');
return Parse.Promise.as()
.then(function() {
return Parse.Object.saveAll([])
.then(function() {
a += 1
return Parse.Promise.as(a);
})
})
}).then(function(a){
return myPromise(a);
})
.then(function(davidBowie) {
console.log(davidBowie.name);
console.log(davidBowie.a);
})
.fail(function() {
// handle error
})