Wait for 1 promise and then all promises using $q - javascript

I'm fairly familiar with how $q works and I use it in angularjs to wait for single promises to resolve and multiple promises to resolve with $q.all().
The question is I'm not sure if its possible to do this (and if it works correctly): Can I wait for a single promise to resolve, but then also run some code when all my promises resolve too... AFTER the success callbacks of the individual promises have finished... for example:
var promises = [];
for(i=1, i<5, i++){
var singlePromise = SomeSevice.getData();
promises.push(singlePromise);
singlePromise.then(function(data){
console.log("This specific promise resolved");
});
}
// note: its important that this runs AFTER the code inside the success
// callback of the single promise runs ....
$q.all(promises).then(function(data){
console.log("ALL PROMISES NOW RESOLVED"); // this code runs when all promises also resolved
});
My question is, does this work as I think it does, or is there some weird async, indeterministic result risk?

A call to then also returns a promise. You can then pass this to your array instead of the original promise. This way your $q.all will run after all your thens have been executed.
var promises = [];
for(i=1, i<5, i++){
// singlePromise - this is now a new promise from the resulting then
var singlePromise = SomeSevice.getData().then(function(data){
console.log("This specific promise resolved");
});
promises.push(singlePromise);
}
$q.all(promises).then(function(data){
console.log("ALL PROMISES NOW RESOLVED");
});

Related

Resolving promises synchronously with Q.all(promises)

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.

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)
});

How to have single finally handler for multiple promises in AngularJS?

I have two promises, independent of each other and I want to do a finally step after both of the promises are completed (either rejects or resolves).
$q.all() cannot be used as I want the finally to always execute (only once for both promises) even if any of the promises rejects
One solution could be to have a counter and have a finallyHandler function to execute it when both promises resolves/rejects.
var count = 0;
function finallyHandler() {
if ( ++count === 2 ) {
doStuff();
count = 0;
}
}
firstPromise.finally(finallyHandler);
secondPromise.finally(finallyHandler);
This can work but doesn't look right.
Is there any better way to do this?
Promise.all(iterable) will do what you're looking for. Please have a look at the browser compatibility, however, as Internet Exploer does NOT support this method. From the documentation:
The Promise.all(iterable) method returns a promise that resolves when
all of the promises in the iterable argument have resolved, or rejects
with the reason of the first passed promise that rejects.
Using your code example (with failure behavior), it would look like this:
Promise.all([firstPromise, secondPromise]).then(values => {
finallyHandler();
}, reason => {
finallyHandler();
});
Even though the failure block is the same as the success block, the success block only runs once all promises have completed successfully, and the failure block runs once (and only once) the first failure is encountered.
Reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Normally $q.all resolves rejected on the first promise that gets rejected and doesn't wait for the other promises. To wait for all promise to resolve either fulfilled or rejected, make resiliant versions of each promise, then use $q.all on those:
var resiliantFirstPromise = firstPromise.catch(e => "CONVERTED");
var resiliantSecondPromise = secondPromise.catch(e => "CONVERTED");
$q.all([resiliantFirstPromise, resiliantSecondPromise])
.then(() => {
console.log("Both promises have resolved");
});
By returning a value to the .catch method, each promise that is rejected returns a derived promise that has been converted to a fulfilled promise. Since neither derived promise is rejected, the $q.all will wait for both promises to be resolved (either fulfilled or rejected.)
firstPromise().catch().secondPromise().catch().then(//what you want)
The above promise chain can achieve this. Final then would be called in all scenarios . catch would propagate the promise rejections.
Can you use?
Promise.All([array of promises],handler)
Promise.All Docs
Or libraries like BlueBird or Asyncjs?

Promise.all() and understanding when it resolves

I'm newer to node.js and writing a program that is going to require promises for async API calls. I have a question regarding the execution of some example code that I have stumbled across in my research.
The code below (from my understanding) will hit an API, wait for the response, then resolve that response as a promise. This is done iteratively and every created promise is passed into an array of promises. Eventually Promise.all() is called on the array of promises and .then() more code will execute to iterate over the array and add the image to the page.
function getMovie(title) {
return new Promise(function(resolve, reject) {
var request = new XMLHttpRequest();
request.open('GET', 'http://mymovieapi.com/?q=' + title);
request.onload = function() {
if (request.status == 200) {
resolve(request.response); // we get the data here, so resolve the Promise
} else {
reject(Error(request.statusText)); // if status is not 200 OK, reject.
}
};
request.onerror = function() {
reject(Error("Error fetching data.")); // error occurred, so reject the Promise
};
request.send(); // send the request
});
}
function fetchMovies() {
var titles = document.getElementById('titles').value.split(',');
var promises = [];
for (var i in titles) {
promises.push(getMovie(titles[i])); // push the Promises to our array
}
Promise.all(promises).then(function(dataArr) {
dataArr.forEach(function(data) {
var img = JSON.parse(data)[0].poster.imdb;
document.getElementById('movies').innerHTML = document.getElementById('movies').innerHTML + '<img src="' + img + '"/>';
});
}).catch(function(err) {
console.log(err);
});
};
fetchMovies();
What I'm not understanding here is how Promise.all() waits for ALL of the API responses to be pushed into promises. Since getMovie(title) resolves every promise before it is pushed into the promises array, shouldn't it be the case that the first resolved promised that is pushed in causes the Promise.all() section to execute (as 1 of 1 promises in the array are resolved)?
I think the confusion comes where you say "Since getMovie(title) resolves every promise before it is pushed into the promises array".
This is not what is happening. Notice the return statement. The getMovie function returns a Promise object immediately, and resolves when the resolve (or reject) function is called at some later time (typically), in this case after an asynchronous call.
So it first returns a promise, then at a later time the promise resolves (or rejects). And Promise.all waits for an array of these promises to resolve.
By the time you call Promise.all, the array has been filled with the Promise objects. Promise.all does the equivalent of calling .then on all of the promises. When all of the promises are resolved, or one rejects, your handlers get called.

jQuery's "$.when().done()" for AngularJS

I was looking for some way to know when two or more ajax calls have finished using AngularJS but I was only able to find it using jQuery:
$.when(promise3, promise5).done(
function(threeSquare, fiveSquare) {
console.info(threeSquare, fiveSquare);
}
);
and:
var promises = [
$.getJSON('square/2'),
$.getJSON('square/3'),
$.getJSON('square/4'),
$.getJSON('square/5')
];
$.when.apply($, promises).done(function() {
console.info(arguments);
});
is there any way to do something like this using AngularJS?
Thanks for your support!
You will want to look at $q.all(); You can read about it here.
Here is a snippet:
all (promises)
Combines multiple promises into a single promise that is resolved when
all of the input promises are resolved.
Parameters promises – {Array.} – An array of promises.
Returns {Promise} – Returns a single promise that will be resolved
with an array of values, each value corresponding to the promise at
the same index in the promises array. If any of the promises is
resolved with a rejection, this resulting promise will be resolved
with the same rejection.
In version 1.1.x, you should be able to do something like this:
$q.all([
$http.get('square/2'),
$http.get('square/3'),
$http.get('square/4'),
$http.get('square/5')
]).then( function(arrayOfResponses) {
$log.info('all set');
$log.debug(arrayOfResponses);
});
Update:
As of version 1.1.6, you should be able to do the following:
var Square = $resource('square/:id', {id:1});
$q.all([
Square.get({id:2}).$promise,
Square.get({id:3}).$promise,
Square.get({id:4}).$promise,
Square.get({id:5}).$promise
]).then(function(arrayOfResponses) {
$log.info('all set');
$log.debug(arrayOfResponses);
});

Categories

Resources