I have an object:
This is the function I use to loop through the object:
function getAllUsersKeys(user){
var promise = new Promise((resolve, reject) => {
var tokens = []
user.forEach(function(user) {
admin.database().ref(`/FAVORITES/${user.key}`).orderByKey().once('value').then((favShops) => {
favShops.forEach((shop)=>{
if(shop.key==saledata.store_id){ //if store that activated sale if found under users list under /FAVORITES
admin.database().ref(`/FAVORITES/${user.key}/token`).orderByKey().once('value').then((token) => {
//console.log(token.val().id);
tokens.push(token.val().id);
})
}
})
})
})
resolve(tokens);
})
return promise;
}
the console message will print out the "id" under selected token.
//console.log(token.val().id);
but when I push this "id" into the array called tokens nothing appears.
tokens.push(token.val().id);
Basically what I want this method to do is return an array with a selected amount of "id".
Please help me optimise my code. Right now it returns an empty array.
The issue is that you are resolving your getAllUserKeys promise before the firebase promises are resolved. The reason your console.log works is because it waits until your firebase lookup is done before logging. When you resolve(tokens), the promises that fill that array haven't resolved yet.
You would need to make sure that all of your firebase promises have resolved before you resolve(tokens).
UPDATE:
I'm not easily able to test this, so it likely won't work first try.
function getAllUserKeys(user) {
return Promise.all(user.map(user => {
return admin.database().ref(`/FAVORITES/${user.key}`).orderByKey().once('value');
})).then(favShops => {
return Promise.all(favShops.filter(shop => {
if(shop.key === saledata.store_id) {
return admin.database().ref(`/FAVORITES/${user.key}/token`).orderByKey().once('value');
}
return false;
}))
}).then(tokens => {
return tokens.map(token.val().id);
})
}
This is an example of how you would chain promises.
Convert an array of users into and array of promises from firebase, this will resolve to favShops.
In the then method you will get the result of the promise all, which should be an array of favShops.
Take the favShops and filter out only the shops that match the saledata.store_id, returning promises that will be resolve by the firebase api.
Once all the filtered shops promises have resolved you will take the array of tokens and convert them into an array of ids.
Use getAllUserKeys().then(tokens => {}) where tokens should be an array of ids.
Related
I'm currently fetching data from an API and I need to do multiple GET requests (using axios). After all those GET requests are completed, I return a resolved promise.
However, I need to do these GET requests automatically based on an array list:
function do_api_get_requests() {
return promise = new Promise(function(resolve, reject) {
API_IDs = [0, 1, 2];
axios.get('https://my.api.com/' + API_IDs[0])
.then(data => {
// Do something with data
axios.get('https://my.api.com/' + API_IDs[1])
.then(data => {
// Do something with data
axios.get('https://my.api.com/' + API_IDs[2])
.then(data => {
// Do something with data
// Finished, resolve
resolve("success");
}
}
}
}
}
This works but the problem is API_IDs isn't always going to be the same array, it will change. So I'm not sure how to chain these requests automatically.
Since you said it may be a variable length array and you show sequencing the requests, you can just loop through the array using async/await:
async function do_api_get_requests(API_IDS) {
for (let id of API_IDS) {
const data = await axios.get(`https://my.api.com/${id}`);
// do something with data here
}
return "success";
}
And, since you said the list of API ids would be variable, I made it a parameter that you can pass into the function.
If you wanted to run all the API requests in parallel (which might be OK for a small array, but might be trouble for a large array) and you don't need to run them in a specific order, you can do this:
function do_api_get_requests(API_IDS) {
return Promise.all(API_IDS.map(async (id) => {
const data = await axios.get(`https://my.api.com/${id}`);
// do something with data here for this request
})).then(() => {
// make resolved value be "success"
return "success";
});
}
Depending upon your circumstances, you could also use Promise.allSettled(). Since you don't show getting results back, it's not clear whether that would be useful or not.
You can use Promise.all() method to do all API requests at the same time, and resolve when all of them resolves.
function do_api_get_requests() {
const API_IDs = [0, 1, 2];
let promises = [];
for (const id of API_IDS) {
promises.push(axios.get(`https://my.api.com/${id}`));
}
return Promise.all(promises);
}
If you use Bluebird.js (a better promise library, and faster than the in-built Promise), you can use Promise.each(), Promise.mapSeries(), or Promisme.reduce() to do what you want.
http://bluebirdjs.com
I need to recursively go through JSON and in some cases call remote API. I need to return the whole JSON modified at the end but I cannot figure out how to wait until all promises are fulfilled
const getObjectsOfRelated = (xmlAsJson, token) => {
if (testIfIwantCallApi()) {
const jsonToReturn = JSON.parse(JSON.stringify(xmlAsJson))
jsonToReturn.elements = callApi(xmlAsJson.text).then(result => {
return result.data
})
return jsonToReturn
}
if (xmlAsJson.elements) {
const jsonToReturn = JSON.parse(JSON.stringify(xmlAsJson))
jsonToReturn.elements = xmlAsJson.elements.map(res => getObjectsOfRelated(res, token))
return jsonToReturn
}
return xmlAsJson
}
Even if I try to hack it using setTimeout the result does not include parts that were created using external API.
This way the code returns correct structure with promises instead of values I want it either return completed promises or be able to wait until the promises are fulfilled.
Wrap plain return values in Promise's using Promise.resolve:
const getObjectsOfRelated = (xmlAsJson, token) => {
if (testIfIwantCallApi()) {
const jsonToReturn = JSON.parse(JSON.stringify(xmlAsJson))
return callApi(xmlAsJson.text).then(result => {
jsonToReturn.elements = result.data;
return jsonToReturn;
})
}
if (xmlAsJson.elements) {
const jsonToReturn = JSON.parse(JSON.stringify(xmlAsJson))
Promise.all(xmlAsJson.elements.map(res => getObjectsOfRelated(res,
token)).then((results) => {
jsonToReturn.elements = results.map(result => result.data);
return jsonToReturn;
});
}
return Promise.resolve(xmlAsJson);
}
This way you will consistently return promises and you can use your function like this:
getObjectsOfRelated(xmlAsJson, token).then(result => console.log(result))
You can use "Promise.all"...
For a simple array, you map a function over the array:
The function returns a promise for the "new value" of each element.
If you use Bluebird promises, you can even return a mixture of Promises and plain values.
( without having to wrap plain values in "Promise.resolve" )
Then you pass the array of promises to "Promise.all()", which waits for all of them to complete.
To transform a tree-shaped data structure (like JSON), you do the same sort of thing, but recursively. Each node in the tree would use "Promise.all" to wait for all its child-nodes=, and the root node would only "resolve" when every node in the tree has resolved.
Note that "Promise.all" is going to run all of your ASYNC functions at the same time. If you don't want that, you can instead use "Promise.mapSeries", which does the same thing, but it waits for each async function before starting the next. This can be better if you have large data and don't want to start too many simultaneous async functions at the same time.
I'm making a request to server to fetch some values and push them in an array
My GraphQL server's return me the array before the process ending, hence returns an empty array. Here a reproduction of the two methods I use :
1-
var array= [];
var auths= authModel.find({}).exec()
.then(res => {
res.map(el =>{
array.push(el.ip)
})
}).then(() => array)
console.log(auths) // object Promise
console.log(array) // []
2-
const auths= authModel.find().exec();
if(!auths){
throw new Error("Error while fetching users...")
}
console.log("auths:", auths) // Promise { <pending> }
return auths // on graphiql > "auths": { "ip": null }
What is going wrong ? How make my element returns when the promise is resolved ?
Any hint would be great,
thanks
If you run something that returns a promise, the value can be referenced in the following parentheses e.g.
.then((data) => data)
Will return the data obtained from the promise.
If you want to do something locally with the returned data, like assign it to a local variable, you can do that within the then.
Alternately if you just need to return the data to the parent function that invoked this resolver function, then just add a return statement to the beginning of the authmodel.find statement as well.
I know this is a hot topic on stackoverflow, but I'm running into an issue while filling an external object with a promise function.
So basically what I want to do:
Through a promise get an array of objects I want to iterate over
Iterate over this array with a map function. Call a promise with each iteration
After this second promise resolves I want to push an Id and the result of the promise to an array
Naturally I cannot use a global object, because the promise will not be in the right scope. I have also experimented with Bluebird Promise.map function, but this way I am not able to push to the object more than only the second promise results.
Here is my code so far. PromiseTwo should populate an object, which I want to show in the res.json function (this is an Express/NodeJS app)
let promise = actie.groupActies()
let akties = {}
promise.then(function(aktieMaanden) {
let values = aktieMaanden.forEach((aktie) => {
let aktieId = aktie['_id']
let artikelen = aktie['artikelen']
let promiseTwo = order.getActieArtikelenOmzet(artikelen)
promiseTwo.then(function(orders) {
akties[aktieId] = orders
return akties
})
})
return akties
}).then((akties) => {
res.json({ message: "Omzet voor aktie", akties: akties })
})
Through a promise get an array of objects I want to iterate over
actie.groupActies()
Iterate over this array with a map function. Call a promise with each iteration
.then( acties => Promise.all(
acties.map(actie =>
order.getActieArtikelenOmzet(actie.artikelen)
.then(orders => [actie._id,orders])
)
))
After this second promise resolves I want to push an Id and the result of the promise to an array
.then(results=> res.json({results}))
The main idea here is to use Promise.all so that it only continues if all orders have finished.
Elaborating on the answer of Jonas w, using Promise.map the following works as well:
actie.groupActies()
.then(acties =>
Promise.map(acties, actie =>
order.getActieArtikelenOmzet(actie.artikelen)
.then(orders => [actie._id,orders])
)
)
.then(results => {
res.json({ message: "Omzet voor aktie", akties: results})
})
I'm working on a college/personal project which takes RSS urls and sends them through a series of APIs. Code follows:
var tempStory = [];
function getFeed () {
$.getJSON('spoonfed/app/scripts/categories.json', function(categories){
for (var category in categories){
if (categories[category][2] === true){
getFeedURLs(categories[category][0]).then(function(rssData) {
for (var item in rssData){
tempStory.push(rssData[item]);
}
});
}
}
});
}
There are an unknown number of categories I need to iterate over in addition to 10 news stories in each category, which are then pushed to the array tempStory. I need to run another function on this array of data. I think promises are the key here but I can't get my head around how I'd use them in this case.
Any help would be really appreciated and I'm happy to provide more of the code if needed, including the structure of the categories.json file and the getFeedURLs function.
If the issue is that you're just trying to figure out how to get all the data out, then you can do this:
function getFeed() {
return $.getJSON('spoonfed/app/scripts/categories.json').then(function (categories) {
var tempStory = [], promises = [];
for (var category in categories) {
if (categories[category][2] === true) {
promises.push(getFeedURLs(categories[category][0]).then(function (rssData) {
for (var item in rssData) {
tempStory.push(rssData[item]);
}
}));
}
}
return $.when.apply($, promises).then(function() {
// return results array as the fulfilled value of the promise
return tempStory;
});
});
}
getFeed().then(function(data) {
// all the data available here
}, function(err) {
// error here
});
The key aspects of this are:
Return the original ajax promise from getFeed().
When iterating the categories, push all those promises into an array to collect them all
Use $.when() with your array of promises to know when all of those are done
From within the $.getJSON.then() handler, return the $.when() promise which will chain to the $.getJSON() promise and let you return an array of results.
From within the $.when().then() handler, return our results so that becomes the fulfilled value of the final promise
Note: because of the way you chose to accumulate the tempStory array, it's results are not in a guaranteed order. Additional code could be written to maintain the order.