I have an array of objects and I am iterating through the array with an async forEach loop and making an HTTP get request with Axios. I tell the piler to wait for axios to finish before proceeding, but for some reason console.log(data) still runs before console.log(ret)
I think this might be because the forEach loop just gets skipped as soomn as it hits the await and continues, but I don't know how to fix this issue
data.forEach(async (e, i) => {
let req = `/${e.lat},${e.log},${Math.floor(e.start_time / 1000)}?units=si`
let ret = await axios(req)
console.log(ret)
data[i]['weather'] = ret.data.currently.summary
data[i]['cloudCover'] = ret.data.currently.cloudCover
})
console.log(data)
Here is the output that I see (Note that the first array should theoretically have 'weather' and 'cloudCover' attributes, since they are appended)
[ { start_time: 1548952405372,
end_time: 1548953096266,
lat: 59.57644286,
log: 20.16817143 },
{ start_time: 1548958463054,
end_time: 1548959597889,
lat: 59.57644286,
log: 20.16817143 },
{ start_time: 1548964774667,
end_time: 1548966048587,
lat: 59.57644286,
log: 20.16817143 } ]
{ status: 200,
statusText: 'OK',
headers:
{ date: 'Wed, 10 Jul 2019 02:57:13 GMT',
'content-type': 'application/json; charset=utf-8',
'content-length': '10354',
connection: 'close',
'x-authentication-time': '705ms',
'x-forecast-api-calls': '13',
'cache-control': 'max-age=86400',
I have an array of objects and I am iterating through the array with an async forEach loop and making an HTTP get request with Axios. I tell the piler to wait for axios to finish before proceeding, but for some reason console.log(data) still runs before console.log(ret)
I think this might be because the forEach loop just gets skipped as soomn as it hits the await and continues, but I don't know how to fix this issue
data.forEach(async (e, i) => {
let req = `https://api.darksky/forecast/7e12d816d7818a03901fa6a72e6802f5/${e.lat},${e.log},${Math.floor(e.start_time / 1000)}?units=si`
let ret = await axios(req)
console.log(ret)
data[i]['weather'] = ret.data.currently.summary
data[i]['cloudCover'] = ret.data.currently.cloudCover
})
console.log(data)
Here is the output that I see (Note that the first array should theoretically have 'weather' and 'cloudCover' attributes, since they are appended)
[ { start_time: 1548952405372,
end_time: 1548953096266,
lat: 59.57644286,
log: 20.16817143 },
{ start_time: 1548958463054,
end_time: 1548959597889,
lat: 59.57644286,
log: 20.16817143 },
{ start_time: 1548964774667,
end_time: 1548966048587,
lat: 59.57644286,
log: 20.16817143 } ]
{ status: 200,
statusText: 'OK',
headers:
{ date: 'Wed, 10 Jul 2019 02:57:13 GMT',
'content-type': 'application/json; charset=utf-8',
'content-length': '10354',
connection: 'close',
'x-authentication-time': '705ms',
'x-forecast-api-calls': '13',
'cache-control': 'max-age=86400',
Share
Improve this question
edited Jul 10, 2019 at 3:06
Charlie
23.9k12 gold badges63 silver badges95 bronze badges
asked Jul 10, 2019 at 3:03
KavfixnelKavfixnel
3314 silver badges12 bronze badges
2
- Check this out.. developer.mozilla/en-US/docs/Web/JavaScript/Reference/… – Prashanth S. Commented Jul 10, 2019 at 3:08
-
If you want to sequence your async calls to be one at a time sequenced after each other, replace your
.forEach()
loop with a regularfor
loop..forEach()
is not async aware so it doesn't not wait for your callback to resolve it's promise before going on to the next iteration. A regularfor
loop will wait. – jfriend00 Commented Jul 10, 2019 at 5:24
5 Answers
Reset to default 6forEach, in fact, doesn't wait for anything: you've given it an async
function so it can schedule a start call for that and immediately move on to the next function because there is nothing to wait for: as an async function, its return value is a Promise, not real data.
If you want to wait until all your async functions are done, then you'll have to use Promise.all
:
async runThisStuff() {
await Promise.all(data.map(async (e, i) => {
let url = `...`
let ret = await axios(url);
console.log(ret)
data[i]['weather'] = ret.data.currently.summary
data[i]['cloudCover'] = ret.data.currently.cloudCover
});
console.log(data);
}
And if you want to do this in global context, you may not be able to await Promise.all
(older browsers, Node, etc. can only await
inside an async
function), and you'll have to use the older then
that Promises offer:
Promise.all(
data.map(async(...) => { ... })
).then(() => {
console.log(data)
});
All you need to do is using Promise.all
and Array.map
. it will wait all promises to be done. see below.
const newData = await Promise.all(data.map(async(e, i) => {
let req = `https://api.darksky/forecast/7e12d816d7818a03901fa6a72e6802f5/${e.lat},${e.log},${Math.floor(e.start_time / 1000)}?units=si`;
let ret = await axios(req);
console.log(ret);
e.weather = ret.data.currently.summary;
e.cloudCover = ret.data.currently.cloudCover;
return e;
}));
console.log(newData);
The forEach
method makes multiple function calls without waiting for anything regardless of what goes on inside these functions.
The flow inside the functions are effected by await
- but forEach
itself is not.
Use for-in loop for synchronous remote requests.
async function makeCalls() {
console.log('starting');
for (d in data) {
let req = `https://api.darksky/forecast/7e12d816d7818a03901fa6a72e6802f5/${e.lat},${e.log},${Math.floor(e.start_time / 1000)}?units=si`
let ret = await axios(req)
console.log(ret)
d['weather'] = ret.data.currently.summary
d['cloudCover'] = ret.data.currently.cloudCover
}
console.log('ending');
}
forEach is actually synchronous. It takes a callback function as parameter, which in this case is your ASYNC function. So strictly speaking, all the codes were executed but at a different time than you expected them to. None was skipped.
Async/await is just syntactic sugar for Promise. That means that every line of code after "await" in your loop is only executed when the promised is resolved.
A better way to do this could be promise.All() like others have suggested.
you can use for of:
const foo = async () => {
const arr = [1,2,3,4,5,6];
for (let i of arr) {
const response = await // async operations
}
}
foo();