最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - NodeJS await not waiting for HTTP call to finish - Stack Overflow

programmeradmin4浏览0评论

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 regular for 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 regular for loop will wait. – jfriend00 Commented Jul 10, 2019 at 5:24
Add a ment  | 

5 Answers 5

Reset to default 6

forEach, 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();

发布评论

评论列表(0)

  1. 暂无评论