Asynchronous code in nested forEach loop- react native - javascript

I have input data that is formatted as such:
[ [4, 1, 2], [2, 5] ]
I want to make an api call for each of the numbers in the array, and have output as such:
[ [response_4, response_1, response_2], [response_2, response_5] ]
I've been stuck on this logic for two days-- I can't get my return array formatted correctly. It instead returns:
[ response_4, response_1, response _2, response_2, response_5 ]
I know I'm doing something wrong in terms of using promises/async, and also I know I need to reset temp to length = 0 at some point, but every time I add that in, it will simply return [] as my output. Any advice/help?
const getNumData = (data) => {
let temp = []
return new Promise((resolve, reject) => {
data.forEach((outerArray) => {
return new Promise((resolve, reject) => {
outerArray.forEach((number) => {
return fetch(`http://127.0.0.1:8000/api/number?id=${number}`, {method: 'GET',})
.then((response) => response.json())
.then((responseJson) => {
temp = this.state.seqDone.concat(responseJson[0]);
this.setState({
seqDone: temp
})
console.log(temp)
})
})
if (this.state.seqDone) {
console.log(this.state.seqDone)
resolve(this.state.seqDone);
} else {
reject(Error('Sequences not found'));
}
})
});
if (this.state.seqDone) {
console.log(this.state.seqDone)
resolve(this.state.seqDone);
} else {
reject(Error('Sequences not found'));
}
})
}

You can do it in this way
const nestedPromise = async (items = []) => {
return await Promise.all(
items.map(async item => {
if (Array.isArray(item) && item.length) {
return await nestedPromise(item)
}
// return await call to your function
return 'response-' + item
})
)
}
const items = [ [4, 1, 2], [2, 5] ]
nestedPromise(items).then(results => {
console.log(results)
})
Promise.all accepts array of functions as arguments, thoses functions will be executed asynchronously. In your case you just have to use it recursively

fetchData = (item) => {
return fetch(`http://127.0.0.1:8000/api/pose?id=${item}`)
.then (response => response.json())
}
constructArray = (items) => {
Promise.all(items.map(nestedArray => {
return Promise.all(nestedArray.map(this.fetchData))
}
))
.then((results) => {
console.log(JSON.stringify(results))
})
}

Related

Get fullData when CollectionGroup is found

I have a problem querying all data from 1 collection
User-> UidUser-> InfoUser (POST (Collection), name, age ...)
I then used the code below to get the collectionsGroup (Post) but how do I get the User's Name and age?
When I use 2 nested loops, another problem is that forEach cannot use Await and Async inside it makes me lose data.
getData = async () => {
await firebase.firestore().collection('user').doc(Fire.shared.uid).onSnapshot(documentSnapshot => {
firebase.firestore().collectionGroup('Post').where('uid', 'in',
documentSnapshot.data().Followings).get().then(querySnapshot => {
const Post = [];
querySnapshot.forEach(post => {
Post.push({ data: post.data() }) // This is correct and get full data's Post
});
this.setState({ dataPost: Post })
})
})
}
await firebase.firestore().collection('user').doc(Fire.shared.uid).onSnapshot(documentSnapshot => {
firebase.firestore().collectionGroup('Post').where('uid', 'in', documentSnapshot.data().Followings).get().then(querySnapshot => {
const Post = [];
querySnapshot.forEach(async (post) => {
// Incorrect because forEach don't wait callback => Lose a documents's user //
await firebase.firestore().collection('user').doc(post.data().id).onSnapshot(user => {
Post.push({ data: post.data(),user: documentSnapshot.data() })
})
});
this.setState({ dataPost: Post })
})
})
I think promise.all resolve this problem
firebase.firestore()
.collection("user")
.doc(Fire.shared.uid)
.onSnapshot((documentSnapshot) => {
firebase
.firestore()
.collectionGroup("Post")
.where("uid", "in", documentSnapshot.data().Followings)
.get()
.then((querySnapshot) => {
const Post = [];
querySnapshot.forEach((post) => {
Post.push(new Promise((resolve, reject) => {
firebase
.firestore()
.collection("user")
.doc(post.data().id)
.onSnapshot((user) => {
resolve({ data: post.data(), user: documentSnapshot.data() });
});
}))
});
Promise.all(Post).then(res => {
console.log(res)
this.setState({ dataPost: res})
})
});
});
I show how it's work with a simple example with setTimeout function & forEach function maybe it helps others when facing this kind of problems
async function call() {
const nums = [1, 2, 3, 4, 5];
const promises = [];
nums.forEach((res) => {
setTimeout(() => {
promises.push(res*2);
}, 10000);
});
console.log(promises);
}
call();
async function call() {
const nums = [1, 2, 3, 4, 5];
const promises = [];
nums.forEach((res) => {
setTimeout(() => {
promises.push(res*2);
}, 10000);
});
Promise.all(promises).then((res) => {
console.log("res", res);
});
}
call();
in the above examples, output was an empty array so I figure out a way to fix this issue
async function call() {
const nums = [1, 2, 3, 4, 5];
const promises = [];
nums.forEach((res) => {
promises.push(
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(res * 2);
}, 3000);
})
);
});
Promise.all(promises).then((res) => {
console.log("res", res);
});
}
call();

Get data from first .then based on condition on second .then

I used promises as advised in my previous question to get values from 2 async calls.
But I want the results from my first call based on a condition of my second call. I keep getting undefined when I do what I am doing. How do I get my desired result.
First JSON:
let first_json = [
{
"company": "one"
},
{
"company": "two"
},
{
"company": "three"
}
]
The second JSON is dependent on the first one and is of similar format.
Using promises I did:
$.getJSON(first_json)
.then(first_data =>
first_data.map(d => {
return d.company;
})
)
.then(promises => Promise.all(promises))
.then(company => company.map(c => {
let second_json = json_string + c;
$.getJSON(second_json, function(data) {
if (data.length > 0) return c;
});
}))
.then(arr => {
console.log(arr);
});
arr for me is supposed to return ['one', 'three'] but is instead returning:
[undefined, undefined, undefined].
Why is that happening and how do I fix it?
Your callback is asynchronous, so, unless you 'await' it with a then, it won't be available to you right away, and therefore you can't act based on it.
Instead, do it like this:
$.getJSON(first_json)
.then(first_data =>
first_data.map(d => {
return d.company;
})
)
.then(promises => Promise.all(promises))
.then(company => company.map(c => {
let second_json = json_string + c;
return $.getJSON(second_json)
.then(data => {
if (data.length > 0) return c;
});
}))
.then(promises => Promise.all(promises))
.then(arr => {
console.log(arr);
});
You're applying the Promise.all in the wrong stage:
$.getJSON(first_json).then(first_data => {
const companies = first_data.map(d => {
return d.company;
});
const promises = companies.map(c => {
// ^^^^^^^^
let second_json = json_string + c;
return $.getJSON(second_json).then(data => {
// ^^^^^^
if (data.length > 0) return c;
});
});
return Promise.all(promises);
// ^^^^^^^^^^^
}).then(arr => {
console.log(arr);
});

Returning data out from promise then chain

I have a class method (for 'apollo-datasource-rest' that is supposed to fetch a plan from a plan_id. I need to hit two endpoints and combine the data. This works with Promise.all([promise1, promise2]) and then passes the user_ids on to the next Promise.all method that calls the GET /users endpoint multiple times. If I console.log out the usersArray that is returned, I get the array of users, but if I try to return that array, it doesn't get assigned to the objToReturn variable. I also need to add data from snapshot2 to the objToReturn but that is secondary.
getPlanById = async ( planId ) => {
const promise1 = new Promise((resolve) => {
return resolve(this.get('/url1'))
});
const promise2 = new Promise((resolve) => {
return resolve(this.get('/url2'))
});
const objToReturn = await Promise.all([promise1, promise2])
.then(([snapshot1, snapshot2]) => {
return snapshot1.user_ids
})
.then((userIds) => {
this.getUsersFromUserIds(userIds).then((usersArray) => {
console.log(usersArray)
// return usersArray doesn't assign to objToReturn
})
})
return objToReturn
}
getUsersFromUserIds(userIds) {
let userPromises = []
userIds.forEach((uid) => {
const promise = this.get(`/users/${uid}`)
.then((response) => {
if (response.status === 'success') {
return response.data.user
} else {
return null
}
})
userPromises.push(promise)
})
return Promise.all(userPromises).
then((userPromiseData) => {
return userPromiseData
})
}
You need to return the promise of this.getUsersFromUserIds(userIds). So that the promise chain can work.
E.g.
index.js:
class UserAPI {
async getPlanById(planId) {
const promise1 = Promise.resolve({ user_ids: [1, 2] });
const promise2 = Promise.resolve();
const objToReturn = await Promise.all([promise1, promise2])
.then(([snapshot1, snapshot2]) => {
return snapshot1.user_ids;
})
.then((userIds) => {
// You need to return here
return this.getUsersFromUserIds(userIds).then((usersArray) => {
return usersArray;
});
});
return objToReturn;
}
async getUsersFromUserIds(userIds) {
return [
{ id: 1, name: 'a' },
{ id: 2, name: 'b' },
];
}
}
const userAPI = new UserAPI();
userAPI.getPlanById(1).then((objToReturn) => {
console.log('objToReturn: ', objToReturn);
});
The output in the console:
objToReturn: [ { id: 1, name: 'a' }, { id: 2, name: 'b' } ]

How do I resolve this promise after a for-loop of asynchronous code finishes?

Can't seem to figure out how to make this work without the setTimeout.
I want to console log my map only after the asynchronous PostgreSQL stuff is finished and the map contains all the key/value pairs it should.
const map = new Map();
pool.query(`SELECT * FROM trips WHERE destination = $1 AND begin_time >= $2 AND begin_time < $3 ORDER BY begin_time`, ['BROWNSVILLE ROCKAWAY AV', '2018-07-18 00:00-04:00', '2018-07-19 00:00-04:00'])
.then(res => {
return new Promise((resolve, reject) => {
const { rows } = res;
resolve(rows);
});
})
.then(res1 => {
return new Promise((resolve, reject) => {
for (let i = 0; i < res1.length; i++) {
if (res1[i + 1]) {
pool.query(`SELECT * FROM get_hwtable($1, $2)`, [res1[i].trip_id, res1[i + 1].trip_id]).then(res => {
const { rows: hwRows } = res;
map.set([res1[i].trip_id, res1[i + 1].trip_id], hwRows);
}).catch(e => console.log('20', e));
}
}
setTimeout(() => {
resolve(map);
}, 8000);
});
})
.catch(e => console.log('25', e))
.finally(function () {
console.log(map);
});
You can simply use Promise.all on an array of promises returned by pool.query
const map = new Map();
pool.query(`SELECT * FROM trips WHERE destination = $1 AND begin_time >= $2 AND begin_time < $3 ORDER BY begin_time`, ['BROWNSVILLE ROCKAWAY AV', '2018-07-18 00:00-04:00', '2018-07-19 00:00-04:00'])
.then(({rows}) => rows)
.then(res1 => Promise.all(res1.map((r, i) => {
if (res1[i + 1]) {
return pool.query(`SELECT * FROM get_hwtable($1, $2)`, [r.trip_id, res1[i + 1].trip_id])
.then(res => {
const { rows: hwRows } = res;
map.set([res1[i].trip_id, res1[i + 1].trip_id], hwRows);
}).catch(e => console.log('20', e))
}
})))
.catch(e => console.log('25', e))
.finally(function () {
console.log(map);
});
there are unnecessary constructs in your code. In first then( res => {..} ),
there is no need to return a Promise. You can do
```
pool.query().then(res => {
const {rows} = res;
return rows;
}.then ....
```
If you are already using async you should use await and use this for the whole code block. Could write a promisified timeout function too.
```
///returns a promise that resolves after `duration`
function timeoutPromise(duration) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration)
})
}
async function doSomething() {
const res = await pool.query()
const {rows} = res;
for (...) {
let hwRows = await pool.query(`SELECT * FROM get_hwtable($1, $2)`, [res1[i].trip_id, res1[i + 1].trip_id]);
map...
}
await timeoutPromise(5000)
}
doSomething()
```
Of course the above might make the user wait too long because the asynchronous operations (let hwRows = await pool.query()) will be executed one after another. use Array.map to return an array of Promise and then use Promise.all([Promise]) to get the values.
```
async function doSomething() {
const res = await pool.query()
const {rows} = res;
let hwRowsPromises = rows.map((row) => {
// ...
// return the promise
return pool.query(`SELECT * FROM get_hwtable($1, $2)`, [res1[i].trip_id, res1[i + 1].trip_id])
})
let hwRowsResults = Promise.all(hwRowsPromises)
await timeoutPromise(5000)
}
```
4. Promise.all resolves the values resolved by promises in an array, so you can use convenience like flatten to flatten the array.
```
_.flatten([1, [2, [3, [4]], 5]]);
// => [1, 2, [3, [4]], 5]
```
References
Promise.all(iterable)
Array.prototype.map
async/await

How return a value after a forEach loop using Promises?

I need to know how to return a value after a forEach loop using Promises. In this moment, when I launch my main, I get :
[ Promise { <pending> }, Promise { <pending> } ]
(my sampleidlist contains only 2 records)
This is my code :
MongoClient.connect("mongodb://127.0.0.1/myproject", function(err, db) {
return db.collection('RUN').find({
"idRun": query.idRun
}).toArray()
.then((out) => {
var sampleidlist = out[0].SAMPLE_ID
var pazlist = []
// Promisearr is the array of promises where I try to push the promises
var Promisearr = []
// there is the function find_paz that return idPaz for every sampleId in sampleidlist
function find_paz(sampleid) {
// I return a new Promise for every sampleId
// I want to create an array of idPaz
return new Promise((resolve, reject) => {
db.collection('PATIENTS').find({
"SAMPLE_ID": sampleid
}).toArray()
.then((pazArr) => {
var singlepaz = []
singlepaz.push(pazArr[0].idPaz)
return singlepaz
})
.then((singlepaz) => {
pazlist.push(singlepaz)
})
})
}
// Here the forEach loop
sampleidlist.forEach(sampleid => {
Promisearr.push(
find_paz(sampleid)
)
})
Promise.resolve(Promisearr)
.then(Promise.all(Promisearr))
.then(value => {
// value return {promise<pending>}
// I want that value is the array of idPaz
console.log(value)
}).catch((err) => {
console.log('errored', err);
})
}).catch((err) => {
console.log('errored', err);
})
})
Any suggest?
Thank you very much :)
You have it mixed up between Promise.all and Promise.resolve. Here:
return db.collection('RUN').find({
"idRun": query.idRun
}).toArray()
.then((out) => {
var sampleidlist = out[0].SAMPLE_ID
var pazlist = []
var Promisearr = []
function find_paz(sampleid) {
return db.collection('PATIENTS').find({
"SAMPLE_ID": sampleid
}).toArray()
.then((pazArr) => {
var singlepaz = []
singlepaz.push(pazArr[0].idPaz)
return singlepaz
})
.then((singlepaz) => {
pazlist.push(singlepaz)
return;
})
})
}
Promise.all(sampleidlist.map(find_paz))
.then(values => {
//values is an array with all the promises resolved
//pazlist should have your data.
}).catch((err) => {
console.log('errored', err);
})
}).catch((err) => {
console.log('errored', err);
})
Give it a try, let me know if you need clarification or if it doesn't work.
You are using Promise.resolve() and Promise.all() the wrong way. You should just call Promise.all() then .then(), like this :
Promise.all(Promisearr).then(value =>
console.log(value)
)

Categories

Resources