Reputation: 59
How do you execute a function only after all the promises are resolved where you have to wait for async calls in loops within loops?
The code is simplified to the minimum
$scope.book = function(input) {
//Get the cart items from DB
ref.child('/cart/' + userId).once('value').then(function(cartSnap) {
//Loop through the cart items
cartSnap.forEach(function(cartItemSnap) {
var itemId = cartItemSnap.key();
//Get the inventory items from DB that are in the cart
ref.child('/inventory/' + itemId).once('value').then(function(inventoryItem) {
//Loop through booking IDs of inventoryItem
inventoryItem.child('rentals').forEach(function(rentalSnap) {
var rentalId = rentalSnap.key();
//Get bookings from rental/active
ref.child('/rental/'+ rentalId).once('value').then(function(activeRentals) {
checkIfBookingIsAllowed();
});
});
});
});
//Once everything was checked
bookRental();
});
};
To improve speed all the requests can be made in parallel but the final function bookRental() can only be called when everything is resolved.
Thanks for your help.
EDITED: Another try that failed. The Promise.all('collector') gets fired before all the promises are resolved. So 'done' shows up before all the 'checks' in the console.
$scope.book = function(input) {
//Get the cart items from DB
ref.child('/cart/' + userId).once('value').then(function(cartSnap) {
//Promise collector
var collector = [];
//Loop through the cart items
cartSnap.forEach(function(cartItemSnap) {
var itemId = cartItemSnap.key();
//Get the inventory items from DB that are in the cart
var promise1 = ref.child('/inventory/' + itemId).once('value').then(function(inventoryItem) {
//Loop through booking IDs of inventoryItem
inventoryItem.child('rentals').forEach(function(rentalSnap) {
var rentalId = rentalSnap.key();
//Get bookings from rental/active
var promise2 = ref.child('/rental/'+ rentalId).once('value').then(function(activeRentals) {
console.log('check');
collector.push(promise2);
});
});
collector.push(promise1);
});
});
//Once everything was checked
Promise.all(collector).then(function() {
console.log('Done');
});
});
};
Upvotes: 2
Views: 886
Reputation: 42530
You're collecting the promises too late. Collect them now, not later. Code inside .then
s runs later.
Here's what runs immediately:
.then
s.Promise.all(collector)
.At this point collector
is still empty, so Promise.all()
completes real fast.
Move the collector.push(promise)
calls outside of the .then
s.
With the above fix, your code should work, but a cleaner approach is to return all promises. While forEach
doesn't allow return values, map
does, so this can be re-written as (simplifying):
Promise.all(cartSnap.map(snap => ref.child(itemUrl).once('value').then(item =>
Promise.all(item.child('rentals').map(snap => ref.child(url).once('value'))))))
.then(allActiveRentals => console.log('Done'));
Upvotes: 2