Get Values from array of resolved Promises - javascript

I am using axios.all to call loop through an array of items and make a get request for each so that I can save their data in the correct order. Right now I have an array of Promises, all of which are being resolved with the correct data, and the callback function for when all of these are done is being triggered.
Now I just need to loop through the Promises and save them their values, but I don't know how to access their values!
let promises = [];
for (let report of response.data.res.body.result) {
let dto2 = {
customerId: state.member.customerId,
reportToken: report.reportToken
}
promises.push(axios.post('http://localhost:3001/api/pullReport', dto2));
}
console.log("promises", promises);
axios.all(promises).then(function() {
console.log("done!");
promises.forEach(function(res) {
console.log("res", res);
// commit('SAVE_REPORT', res.value);
})
// resolve('Reports saved.');
});
Here's what each promise looks like when it is consoled in the forEach loop :
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: Object <<<<<<<<<<< NEED THIS
I just need to be able to call the commit('SAVE_REPORT') with the PromiseValue Object, but I don't know what to pass in! I've tried res.value, res.val, res.promiseValue... Is there a secret to this?

axios.all creates a new promise, which will resolve with an array of the results. So to interact with those results, you just need to add a parameter to the function in the .then block:
axios.all(promises).then(function(results) {
results.forEach(function(res) {
commit('SAVE_REPORT', res.value);
});
});

Related

Question about asynchronous JavaScript with Promise

Here I have a function that takes an array of string that contains the user names of github accounts. And this function is going to return an array of user data after resolving. There should be one fetch request per user. and requests shouldn’t wait for each other. So that the data arrives as soon as possible. If there’s no such user, the function should return null in the resulting array.
An example for the input would be ["iliakan", "remy", "no.such.users"], and the expected returned promise after resolving would give us [null, Object, Object], Object being the data that contained info about a user.
Here is my attempt to solve this question.
function getUsers(names) {
return new Promise(resolve => {
const array = [];
const url = "https://api.github.com/users/";
const requests = names.map(name => {
const endpoint = `${url}${name}`;
return fetch(endpoint);
});
Promise.all(requests).then(reponses => {
reponses.forEach(response => {
if (response.status === 200) {
response.json().then(data => {
array.push(data);
});
} else {
array.push(null);
}
});
resolve(array);
});
});
}
It does work, i.e. returning an array [null, Object, Object]. And I thought it fulfilled the requirements I stated above. However, after looking at it closely, I felt like I couldn't fully make sense of it.
My question is, look at where we resolve this array, it resolved immediately after the forEach loop. One thing I don't understand is, why does it contain all three items when some of the items are pushed into it asynchronously after the json() is finished. what I mean is, in the case where response.status === 200, the array is pushed with the data resolved from json(), and I would assume this json() operation should take some time. Since we didn't resolve the array after json() operation is finished, how come we still ended up with all data resolved from json()?
Promise.all(requests).then(reponses => {
reponses.forEach(response => {
if (response.status === 200) {
response.json().then(data => {
array.push(data); <--- this should take some time
});
} else {
array.push(null);
}
});
resolve(array); <--- resolve the array immediately after the `forEach` loop
});
});
It looks to me like the array we get should only have one null in it since at the time it is revolved, the .json() should not be finished
You're right, the result is pushed later into the array.
Try to execute this:
const test = await getUsers(['Guerric-P']);
console.log(test.length);
You'll notice it displays 0. Before the result is pushed into the array, its length is 0. You probably think it works because you click on the array in the console, after the result has arrived.
You should do something like this:
function getUsers(names) {
const array = [];
const url = "https://api.github.com/users/";
const requests = names.map(name => {
const endpoint = `${url}${name}`;
return fetch(endpoint);
});
return Promise.all(requests).then(responses => Promise.all(responses.map(x => x.status === 200 ? x.json() : null)));
};
You should avoid using the Promise constructor directly. Here, we don't need to use it at all.
const url = "https://api.github.com/users/";
const getUsers = names =>
Promise.all(names.map(name =>
fetch(url + name).then(response =>
response.status === 200 ? response.json() : null)));
getUsers(["iliakan", "remy", "no.such.users"]).then(console.log);
The Promise constructor should only be used when you're creating new kinds of asynchronous tasks. In this case, you don't need to use the Promise constructor because fetch already returns a promise.
You also don't need to maintain an array and push to it because Promise.all resolves to an array. Finally, you don't need to map over the result of Promise.all. You can transform the promises returned by fetch.
The thing is that because json() operation is really quick, especially if response data is small in size it just has the time to execute. Second of all as objects in JavaScript passed by reference and not by value and Array is a object in JavaScript, independently of execution time it'll still push that data to the array even after it was resolved.

Firebase Promises in array not resolving in time

I am trying to return 3 promises from my firebase DB and once all three promises have been fulfilled I basically want to render a new page or do whatever. So I do a Promise.All(...) but my lists are still empty afterwards.
My queries are correct because when I console.log() within each of those functions, I get the objects returned from my DB but my Promise.All isn't waiting for those promises to resolve and instead executes the code within the Promise.All which is returning empty lists.
app.get('...', function (req, res) {
//Return first promise from DB save to zone_obj list
var zone_key = req.params.id;
var zone_obj = [];
firebase.database().ref(...).once('value').then((snapme) => {
zone_obj.push(snapme.val());
});
//Return second promise from DB save to members list
var members = [];
firebase.database().ref(...).on("value", function (snapshott) {
snapshott.forEach((snapper) => {
members.push(snapper.val());
});
});
//Return third promise from DB save to experiences list
var experiences = [];
firebase.database().ref(...).on("value", function (snapshot) {
snapshot.forEach((snap) => {
firebase.database().ref(...).once("value").then((snapit) => {
experiences.push(snapit);
});
});
});
//once all promises have resolved
Promise.all([experiences,zone_obj,members]).then(values => {
console.log(values[0]); //returns []
console.log(values[1]); //returns []
console.log(values[2]); //returns []
});
});
This is not actually firebase's problem. Firebase method .on("value") is actually a listener that will be bound to firebase to get real-time updates and that is not actually a promise and your callback function will be called every time when data on that node is changed. so if you want to save or get data only once use firebase.database().ref(...).set() and firebase.database().ref(...).once() method respectively.
According to firebase docs
On method
on(eventType, callback, cancelCallbackOrContext, context) returns function()
once method
once(eventType, successCallback, failureCallbackOrContext, context) returns firebase.Promise containing any type
So change your code to following
app.get('...', function (req, res) {
var promises = []
//Return first promise from DB save to zone_obj list
promises.push(firebase.database().ref(...).once('value'));
//Return second promise from DB save to members list
promises.push(firebase.database().ref(...).once('value'));
//Return third promise from DB save to experiences list
promises.push(firebase.database().ref(...).once('value'));
//once all promises have resolved
Promise.all(promises).then(values => {
console.log(values[0]); // zone_obj
console.log(values[1]); // members
console.log(values[2]); // experiences
});
});

Returning array from promise is undefined

I have a function that calls the iTunes API, and I return an array of objects from that. My main function calls this (and other promises) and waits for all promises to complete. This works, however, for the iTunes API promise the array returned is always "undefined".
My promise:
function getiTunesMusic() {
var options = {
uri: "https://itunes.apple.com/lookup?id=62820413&entity=song&limit=10",
json: true
}
retrieve(options) // This does a GET request
.then(repos => {
var result = repos.results
result.shift() // I get the array of results, minus the first result
console.log(result) // This prints out the full array of song objects
return result
})
.catch((error) => {
return null
})
}
My code waiting for the promises completion block:
Promise.all(promises)
.then(objects => {
var music = objects[0]
console.log("music", objects[0]) // This prints out "music undefined"
profile.music = music
}
The weird thing is when I print out the iTunes api result that I'm returning in the promise, it prints fine. However, in the promise completion block it's always undefined. How do I solve this?
You are not returning the promise from the function, hence, the default return undefined is returned
Try
return retrieve(options) // this returns the promise
in place of
retrieve(options)

Asynchronous function call inside for loop

I am having a problem trying to make my for loop behave synchronously when I am making an asynchronous call in each iteration of the loop.
Here is my code:
pipeline: function(userContext, options, done){
var orderData = [];
options.data.forEach(function(store, index){
store.orders.forEach(function(order){
shopify.getPipeline(userContext, {'order':order,'storeId':index}, function(result){
var snapshotId = "30775bf1bb854c5d84c9c2af37bc8fb0";
var resourceToQuery = config.structrUrls.getUrl("ConfigurationSnapshot") + '/' + snapshotId ;
var requestOptions = {
method: "GET",
timeout: 8000
};
requestify.request(resourceToQuery, requestOptions)
.then(function(snapshotResult){
result.snapshots = snapshotResult.getBody().result;
result.snapshots.configurationPayload = JSON.parse(snapshotResult.getBody().result.configurationPayload);
orderData.push(result);
})
.catch(function(err){
console.log (err);
done(err);
});
});
});
});
done(null, orderData);
}
I understand the problem here, but do not know how to remedy it. Let me explain the function:
options.data contains an array of stores, and each store contains an array of orders. For each order, I am calling shopify.getPipeline() for pipeline data (this is a synchronous operation), and in the callback I make a requestify request (a node module used for making http requests) for snapshot data, which I want to append to the result before pushing it onto my "orderData" array. When this all completes, I am calling "done", a callback function, with my orderData. As you can see, since requestify calls are asynchronous, done is called before any data is added to the orderData array.
I think I need to use some kind of promise in order to guarantee the result before calling done, but I have been unsuccessful in implementing a promise into this function. In the documentation for q, it seems like the function I would want to use is promise.all(), which 'Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected'. I'm failing to see how to translate my forEach loop into an array of promises. I was also looking at the async parallel function and ran into the same problem regarding the array of functions.
Any help is greatly appreciated. Thanks!
To construct an array of Promises for use in Promise.all, you can map across the array of stores, and again across the array of orders, returning a Promise for each order which will be resolved or rejected based on the result of requestify.request. Merging the resulting nested array gives you a single array of promises which can then be passed to Promise.all.
Using your example:
pipeline: function(userContext, options, done){
var nestedPromises = options.data.map.forEach(function(store, index){
return store.orders.map(function(order){
return new Promise(function(resolve, reject){
shopify.getPipeline(userContext, {'order':order,'storeId':index}, function(result){
var snapshotId = "30775bf1bb854c5d84c9c2af37bc8fb0";
var resourceToQuery = config.structrUrls.getUrl("ConfigurationSnapshot") + '/' + snapshotId ;
var requestOptions = {
method: "GET",
timeout: 8000
};
requestify.request(resourceToQuery, requestOptions)
.then(function(snapshotResult){
result.snapshots = snapshotResult.getBody().result;
result.snapshots.configurationPayload = JSON.parse(snapshotResult.getBody().result.configurationPayload);
resolve(result);
})
.catch(function(err){
reject(err);
});
});
});
});
});
// Flatten nested array.
var promises = Array.prototype.concat.apply([], nestedPromises);
Promise.all(promises).then(function(orderData){
done(null, orderData);
}).catch(function(err){
done(err);
});
}

Chaining Asynchronous Functions Node.js bluebird mongoskin

I have been reading many posts on how to chain asynchronous functions but I just can't seem to get it right!
As the title indicates. I am trying to chain mongoskin database calls together, so that i can gather all the information in chunks and then finally send the accumulated result in the response.
I have the object user as :
var User = {
username: 'someusername',
accounts: [{name: 'account_1'}, {name: 'account_2'}]
}
For each of the accounts I need to gather data and then send the accumulated data in the response. So i am using the following for loop to iterate over the accounts:
var promise = require('bluebird');
var db = require('mongoskin').db('mongodb://localhost/someDB');
for(var x in user.accounts){
//Fetch account data
user.accounts[x].accountData = fetchAccountData(user.accounts[x].name);
}
//Finally send the collected response
response.send(user);
And the function fetchAccountData looks like the following:
function fetchAccountData(screen_id){
db.collection('master')
.aggregate([
{$match: {screen_id: screen_id}}
], function(err, res){
if(err)
return null;
else{
console.log('Done', screen_id);
return res;
}
});
}
How can i chain this to have the following algorithm:
start:
for each account:
fetchDataForAccount
Finally:
Send Response
Your algorithm can be achieved using the following code:
var Promise = require('bluebird');
var mongo = require('mongoskin'), db;
Promise.promisifyAll(mongo.Collection.prototype);
db = mongo.db('mongodb://localhost/someDB');
Promise.all(user.accounts.map(function(acct) {
return fetchAccountData(acct.name).then(function(data) {
acct.accountData = data;
});
}))
.then(function() {
response.send(user);
})
.catch(function(err) {
// handle error
});
function fetchAccountData(screen_id){
return db
.collection('master')
.aggregateAsync([
{$match: {screen_id: screen_id}}
]);
}
EDIT: Here's a breakdown of the code
The first thing you need to do is ensure that aggregate returns a Promise instead of using a continuation (e.g. callback). You can do this by using bluebird's amazing promisification abilities :) Here we use it on mongo.Collection.prototype so that when collection() is called it will return a promise-capable collection instance. Then we have fetchAccountData return the promise returned by aggregateAsync so the client has a way of knowing when that promise is resolved.
Next, we map over each account in accounts and return a promise which will be fulfilled once the account data is fetched and it has been assigned to the account object. We then use Promise.all which will return a promise that is fulfilled "when all the items in the array are fulfilled" (from the docs).
Finally, we have to use then() to "wait" until the promise returned from all has resolved, and the finally send back the response with the complete user object.

Categories

Resources