I have three methods (each is a promise) that does SELECT from the database.
getDeliverDate()
.then(calculateOrderList)
.then(getOrderItemsForOrder)
For each deliverDate, I need to get the corresponding list of orders, and for each order I need to get the list of items. I will then build a JSONview to send to the client. To do that, I think I need to wrap the code above with a for loop, and presumably have a "global" object made of arrays etc.. and have to push to the object data from each method. I am not really sure if the approach will work. Can someone please tell me how to get it to work using the code above as an example.
Push all the promises into an array then use Promise.all to wait for all promises to finish and return the result. Something like
var promises = []
for (...) {
promises.push(getDeliverDate()
.then(calculateOrderList)
.then(getOrderItemsForOrder));
}
return Promise.all(promises);
Edit, all not when -- got my promise libraries confused
Better way to do that is use Promise.all:
Promise.all([getDeliverDate, calculateOrderList, getOrderItemsForOrder])
.then(values => {
console.log(values);
});
If what I understand is correct (as given in comments):
getDeliverDate()
// get array of delivery dates
.then((deliveryDates = []) => {
// for each delivery date, I need to get corresponding list of orders
const promises = deliveryDates.map(date => calculateOrderList(date));
return Promise.all(promises);
})
// get order list as an array
.then((orderList = []) => {
// for each order I need to get list of items corresponding to that order
const promises = orderList.map(order => getOrderItemsForOrder(order));
return Promise.all(promises);
})
// get item list as an array
.then((items) => console.log('got everything : ', items))
.catch((error) => console.log('error : ', error))
Looks like you need a lot of API calls. Probably we should think about optimizing backend.
Related
I have the following function that retrieves an array of playlists then attempts to build a simplified version in a new array using a reducer on the fetched array.
The first iteration shows that paredPlaylists contains a single element that looks like I expect. On the second, it instead becomes a Promise and then gives the error
paredPlaylists.push is not a function
I understand that the error itself is explained by the resulting array (paredPlaylists) no longer being an array, but a promise. But how does it successfully push the simple object {id: "foo"} onto the array in the first pass then get converted to a promise in the second?
async function getPlaylists(token) {
return await fetch('https://somewhere')
.then((response) => response.json())
.then(async (response) => {
return await response.items.reduce(async (paredPlaylists, playlist) => {
await paredPlaylists.push({
"id": "foo"
})
}, [])
})
}
Note: I'm still pretty new to React and JS and have only partially gotten a grasp on promises and forcing async. I expect I have a number of awaits that are unnecessary. Early attempts did not have so many, I just kept peppering them to hopefully get actual objects to work with vs. promises.
the reduce fn takes an array and run a callback for each item.
If you're working with an array of array consider using the concat
return response.items.reduce( (a, b) => a.concat(b), [])
If it's just an array, then just map your items as u prefer
return response.items.map(playlist => ({ id: playlist.id }) )
I'm working with Cloud Functions for Firebase, and I get a timeout with some of my functions. I'm pretty new with JavaScript. It looks like I need to put a for inside a promise, and I get some problems. The code actually get off from for too early, and I think he make this in a long time. Do you have some way to improve this code and make the code faster?
exports.firebaseFunctions = functions.database.ref("mess/{pushId}").onUpdate(event => {
//first i get event val and a object inside a firebase
const original = event.data.val();
const users = original.uids; // THIS ITS ALL USERS UIDS!!
// so fist i get all users uids and put inside a array
let usersUids = [];
for (let key in users) {
usersUids.push(users[key]);
}
// so now i gonna make a promise for use all this uids and get token's device
//and save them inside a another child in firebase!!
return new Promise((resolve) => {
let userTokens = [];
usersUids.forEach(element => {
admin.database().ref('users/' + element).child('token').once('value', snapShot => {
if (snapShot.val()) { // if token exist put him inside a array
userTokens.push(snapShot.val());
}
})
})
resolve({
userTokens
})
}) // now i make then here, from get userTokens and save in another child inside a firebase database
.then((res) => {
return admin.database().ref("USERS/TOKENS").push({
userTokens: res,
})
})
})
You are making network requests with firebase, so maybe that's why it's slow. You are making one request per user, so if you have 100 ids there, it might as well take a while.
But there's another problem that I notice, that is: you are just resolving to an empty list. To wait for several promises, create an array of promises, and then use Promise.all to create a promise that waits for all of them in parallel.
When you call resolve, you have already done the forEach, and you have started every promise, but they have not been added to the list yet. To make it better, chance it to a map and collect all the returned promises, and then return Promise.all.
var orderItems = userData.shoppingcart;
var totalPrice = 0;
userData.shoppingcart.forEach(function(itemName, i){
_data.read('menuitems', itemName, function(err, itemData){
if(!err && itemData)
{
totalPrice += itemData.price;
if(++i == userData.shoppingcart.length){
// Only here when all the itemNames have been read I should continue
}
}
});
});
As you can see, the call to _data.read is async, because I am reading from a file.
But I need to wait to have all the files read, so I can have the totalPrice calculated. That's why I place that condition [ ++i == userData.shoppingcart.length ].
I am new to javascript and nodejs in general, never was very good programmar at it, but anyway my point is that I am sure this isn't a good approach, what if both files are readed at same time, and then that condition is never ran or the calculation of totalPrice is badly done?
Can someone give me please some guidance on this?
Thank you in advance!
Given that you don't specify what context this is in, I'm going to make a few assumptions:
I assume that _data.read() doesn't already support returning a promise.
I assume that this code needs to either call a callback function or return a promise.
My (somewhat naive) approach to this would be to:
Map the orderItems into Promises for each price of that item.
Map the result into a total
Return that resulting promise OR call the callback.
Here's an annotated example of how you might do it:
// Promise.all takes an array of promises and returns
// a new promise that completes when all the promises in the array are complete.
const promiseOfPrices = Promise.all(
// Here we map all the items in the shopping cart into promises for each of their prices
userData.shoppingcart.map(
// The Promise object takes a single function that it will immediatly call with functions to resolve or
// reject the promise. I suggest reading up on Promises if you're not familiar with them.
itemName => new Promise((resolve, reject) => {
// Here, we have a `reject` and `resolve` function that will each complete the new promise,
// either in success or error respectfully.
// Do the actual read of your file or database or whatever
_data.read('menuitems', itemName, (err, itemData) => {
// If there was an error, reject this promise.
if (err) reject(err);
// Otherwise, we're successful and we resolve with the price of the item
else resolve(itemData.price);
});
})
)
);
// Now, we have a promise (promiseOfPrices) for all the prices of the items in the cart. We use `then` which will
// perform a transform on the result, much like the `map` function on an Array.
const promiseOfTotal = promiseOfPrices.then(
// Here we use the `Array.reduce` function to succinctly sum the values in the array.
arrayOfCartItemPrices => arrayOfCartItemPrices.reduce(
// For each item, reduce calls our function with the current sum and an item in the array. We produce a new
// sum by adding the sum to the item price.
(sum, itemPrice) => sum + itemPrice,
// This is the initial value for sum, 0.
0
)
);
If you can return a promise, and you just want to return the total, then
return promiseOfTotal;
If you have a callback that expects (err, result), then do something like this:
promiseOfTotal.then(
result => callback(null, result),
error => callback(error, null),
)
If you need to do more work on the result, you can do so with another then:
promiseOfTotal.then(
priceSum => {
// Do work here
},
// Optionally handle errors here:
error => {
// Do error handling here.
}
)
Note that by using promises, arrow functions, and array comprehensions (map and reduce) we avoid complex and hard to follow mutation of variables and loops. This is a "functional" style of programming, and while somewhat harder to learn, is generally safer and cleaner than the alternatives. I suggest taking the time to understand how this works, as it'll help you write code that's less likely to have bugs when you're dealing with complex things like asynchronicity.
Finally, I haven't run this code. It may well have a bug or two. Feel free to ask for clarification or if it doesn't work.
Best of luck!
P.S. I didn't go in to using async/await because I think it would be less clear than using the Promises directly, and the use of Promise.all is needed for the parallelism anyways. It's absolutely possible to use them to good effect here, but I'll leave that as an exercise to the OP.
This is how you can read the items in sequence using promises (async/await flavour):
var orderItems = userData.shoppingcart;
let totalPrice = 0;
for (let itemName of userData.shoppingcart) {
const itemData = await _data.read('menuitems', itemName);
totalPrice += itemData.price;
}
This example assumes that _data.read supports async/await. If it doesn't, however, it can "promisified" using the promisify function in nodejs' util module
actually I'm facing a problem with Promises, a miss understanding surely.
I have an array with a list of ids that I use to make an API call that I'm sure it's valid and working. So I have to make foreach element in this list an API call => Promise.
I read about promises and I find this clean way to make it :
clean(){
/** Here, I'm just cleaning invalid ids to avoid a silenced map. I tried the
whole output in the API explorator, and it works well*/
this.facebook.objectIdsToDelete.forEach(element => {
let index = this.facebook.objectIdsToFilter.indexOf(element);
this.facebook.objectIdsToFilter.splice(index, 1);
});
Promise.all(this.facebook.objectIdsToFilter.map(value=>{
return this.facebook.callApiLikes(value);
}))
.then((responses) => {
this.debogger.debogSomething(responses);
})
.catch((reason) => {
this.debogger.debogSomething(reason);
})
}
debogger => it's a popup to show object content.
this.facebook.objecIdsToFilter => is a string[]
This popup do not raise, nothing happens.
Where I'm doing wrong ?
I'm struggling with the following: So i got a list of ids in a user object, i would like to retrieve the list of users, whose those ids belong to. My idea was to use a map to convert the array of ids to an array of users, I came to the obvious wall. since the call is asynchronous y can't expect the users to be there, however yes the promise.
Is there anyway to return this promises in a way that Ember can show the data when they get resolved ? I don't really care when they get resolved. but when they do, that they render the data.
What is happening now is that i get a list of Promises, which is fine, and kind of expected, but how do i tell ember, ok when you do resolve the promises show the data?
Thanks in advance
let users = this.get('user.followingIds').map((followerId) => {
return this.store.findRecord('user', followerId).then((user) => {
console.log('resolved follower ', user.get('name'));
return user;
}).catch((e) => {
console.error('user catch', e);
});
});
this.set('followers', users);
The console log output is
resolved follower james stuart
on onother filter i have this
this.store.query('followers', {}).then((followers) => {
this.set('followers', followers);
}).catch((e) => {
console.error('publisher catch', e);
});
and that works as expected, but its not in a map, this is just to show im displaying the results the right way
Thank u Daniel for the help, i ended up changing the map for a forEach and populating the array
this.get('user.following').forEach((followerId)=>
{
this.store.findRecord('user', followerId)
.then((user) => {
console.log('resolved follower ',user.get('name'));
publishers.pushObject(user._internalModel);
})
.catch((e) => {
console.error('publisher catch', e);
});
});
I had to put the _internalObject thing otherways it wouldn't work took that from here
Dynamically add js object to model array in 1.13