Resolving promises synchronously with Q.all(promises) - javascript

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.

Related

Dynamic array promise sequence

Im getting dynamic (the number of promises can be change each per runtime) array of promises, Now I want that after every resolve or reject
to handle the returned promise before promises array will be finished .
I try with promise all but it's continue after all the promises has done.
if the array (of promises) wasn't be dynamic so I can simply use something like this
but I dont know how much promises I have in the array and I want after every promise fulfilled or ... to proceed with the answer of it and not wait until all the promises will done
firstMethod()
.then(secondMethod)
.then(thirdMethod);
We are using Q ...
Is it possible ?
Update (example )
Lets say I've the promise array
[promise1,promise2,promise3, promiseN]
Now I want that when promise1 finish to process to handle it with then and not wait that all promises will be finished .
The biggest issue here is the array can have N promises and I dont know in RT how much promises I will get until I got this array therefore I cannot use simply then
Update 2 (to clarify a bit more :-) ) The tricky part...
If I Know the size of the array beforehand I can use simply then , 5 entry in the array 5 chain with then but here the tricky part is that I dont know beforehand how much promises I will have in the array of the promises ....
If the goal is to chain each promise with same then but to not wait for all promises to complete, this is just a loop:
for (const promise of promiseArray) {
promise.then(...)
}
If promise results should be joined together in the end, the array can be additionally processed by all:
Promise.all(promiseArray.map(promise => promise.then(...)))
.then(...)
Notice that behaviour for all applies here. If there is uncaught rejection in promise array, only the first rejection will be caught.
This is true for most promise implementations. There may be Q methods that allow do do this easier.
you can use Promise.race()
The Promise.race(iterable) method returns a promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects, with the value or reason from that promise.
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, 'one');
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'two');
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "two"
// Both resolve, but p2 is faster
});
If you don't need to chain promises. You could just do:
var tasks = [[promise1, handler1], [promise2, handler2], [promise3, handler3]];
tasks.forEach(function (task) {
var promise = task[0];
var handler = task[1];
promise.then(handler)
});

Defining then() after promise has been retrieved

I have a question about attaching callback functions to promises in AngularJS.
Suppose I have a service with a function that returns a promise. I make a call to this function and store the promise locally. Then I define a callback function on the promise.
var promise = TestService.get();
console.log('We have a promise!');
promise.then(function (result){
console.log('Here is the result:'+result);
});
In this case, we have a potentially risky situation. If the promise is resolved before we get to promise.then(..., the result is not outputted to the console (until the next digest cycle).
Alternatively, I could write the above code like this:
TestService.get().then(function (result){
console.log('Here is the result:'+result);
});
My question:
Has the risk been mitigated in the second example? And if not, how can I make sure that the promise does not resolve before I have attached a callback?
A slightly more elaborate answer than yes/no would be much appreciated :)
The behavior you are describing does not occur, that can be seen through a simple example. Here we have a simple promise factory which returns a promise which resolves immediately.
'use strict';
var make = function() {
return new Promise(function(resolve, reject) {
resolve(2);
});
};
Then we create a new promise and assign it to a variable
var prom = make();
We can call .then on it as many times as we want. This is because promises are immutable, we don't change the original value by chaining methods on it.
prom.then(a => console.log(a));
// 2
prom.then(a => console.log(a));
// 2
Suppose I have a service with a function that returns a promise. I make a call to this function and store the promise locally. Then I define a callback function on the promise.
No, you are not attaching a callback. When you call the then method you are doing something called promise chaining. Each call to then returns a new promise object that will resolve to the value returned by the previous promise.
For example;
var promise1 = TestService.get();
var promise2 = promise1.then(function(value) {
console.log('service resolved: '+value);
return "Hello World";
});
var promise3 = promise2.then(function(value) {
console.log(value);
});
promise3.then(function(value) {
console.log(value);
});
The above example will output the following.
**some value from TestService**
Hello World
undefined
We don't know who originally resolve the value for the first promise. All we know is that the service returned a promise. From that moment on we can chain the promises by adding more calls to then.
In this case, we have a potentially risky situation. If the promise is resolved before we get to promise.then(..., the result is not outputted to the console (until the next digest cycle).
No, it does not matter when or what digest the promise is resolved. A promise can have it's then method called multiple times even after being resolved. It will continue to resolve to the value as long as it has not been rejected. The decision to resolve or reject a promise is outside the scope of the success or failure callbacks.
You can create a promise, resolve it to a value, wait several digests and add a handler to then and it will still work as expected.
Has the risk been mitigated in the second example? And if not, how can I make sure that the promise does not resolve before I have attached a callback?
Think of promises as containers. They are going to hold the value you expect, and you have to call then to get it. If for what ever reason the value is unavailable you can find out why by using the error callback. The when aspect of promises is purely an asynchronize issue, and the idea is for promises to hide those issues.
JavaScript is not multithreaded, your asynchronous AJAX call isn't actually made by the browser until your code returns.
var promise = TestService.get();
for (var i= 0;i<100000;i++){
console.log(i)
}
console.log('We have a promise!');
promise.then(function (result){
console.log('Here is the result:'+result);
});
Watch this with the network analyzer.

Outer variables not being altered inside Promise.then function

On node while using thinky.js, I am trying to iterate through a loop and add each item to an array. This however, for some reason is not working.
In another place, it is indentical and working, just without a Promise.then function. Why is this not working?
var fixedItems = [];
for (i in tradeItems) {
var item = tradeItems[i];
Item.get(item["id"]).run().then(function(result) {
var f = { "assetid": result["asset_id"] };
console.log(f); // WOrks
fixedItems.push(f); // Doesn't work
});
}
console.log(fixedItems); // Nothing
A Promise represents the future result of a task. In this case you're logging fixedItems before your tasks (the calls to Item.get) have finished working. In other words, the then functions haven't run yet so nothing has been put into fixedItems.
If you want use fixedItems once it contains all of the items, you'll need to wait for all of the promises to resolve.
How you do that depends on the Promise library you're using. This example, with Promise.all, works with many libraries including native ES6 Promises:
// Get an array of Promises, one per item to fetch.
// The Item.get calls start running in parallel immediately.
var promises = Object.keys(tradeItems).map(function(key) {
var tradeItem = tradeItems[key];
return Item.get(tradeItem.id);
});
// Wait for all of the promises to resolve. When they do,
// work on all of the resolved values together.
Promise.all(promises)
.then(function(results) {
// At this point all of your promises have resolved.
// results is an array of all of the resolved values.
// Create your fixed items and return to make them available
// to future Promises in this chain
return results.map(function(result) {
return { assetid: result.asset_id }
});
})
.then(function(fixedItems) {
// In this example, all we want to do is log them
console.log(fixedItems);
});
Recommended reading: the HTML5 rocks intro to Promises.
Your problem is that you are calling console.log(fixedItems) before any of the promises in the loop have finished executing. A better way of doing this that would also solve the asynchronous problem is to put all the item IDs in an array first and retrieve all the items in a single query, which is also more efficient on the database side.
var itemIds = tradeItems.map(function(item) {
return item.id;
});
var fixedItems = [];
//you would need to write your own getItemsById() function or put the code
//to get the items here
getItemsById(itemIds).then(function(items) {
items.forEach(function(item) {
var f = { "assetid": result["asset_id"] };
fixedItems.push(f);
});
whenDone();
});
function whenDone() {
//you should be able to access fixedItems here
}
I couldn't easily find how to look up multiple records by ID in a single query with thinky, but I did find this page which might help:
http://c2journal.com/2013/01/17/rethinkdb-filtering-for-multiple-ids/
While this would be my preferred way of solving this problem, it would also be possible to still use multiple queries and use a promise chain to wait for them all to be resolved before continuing to your subsequent code. If you wanted to go that route, check out this link: http://promise-nuggets.github.io/articles/11-doing-things-in-parallel.html. (Note: I haven't personally used Bluebird, but I think the Bluebird example in that link may be out of date. The map method appears to be the current recommended way to do this with promises: https://stackoverflow.com/a/28167340/560114.)
Update: Or for this latter option, you can just use the code in joews's answer above.

Promise.all on same array

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.

Q.all with chained sequence

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

Categories

Resources