我有一个对象数组,我正在使用异步 forEach 循环迭代该数组,并使用 Axios 发出 HTTP get 请求。我告诉编译器等待 axios 完成后再继续,但由于某种原因 console.log(data) 仍然在 console.log(ret) 之前运行
我认为这可能是因为 forEach 循环在遇到等待并继续时就被跳过,但我不知道如何解决这个问题
data.forEach(async (e, i) => {
let req = `https://api.darksky.net/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)
这是我看到的输出(请注意,第一个数组理论上应该具有“weather”和“cloudCover”属性,因为它们是附加的)
[ { 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',
forEach 不会等待 anything:您已经给了它一个
async
函数,因此它可以为此安排启动调用,并立即转到下一个函数,因为没有什么可以等待的for:作为一个异步函数,它的返回值是一个 Promise,而不是真正的数据。
如果您想等到所有异步函数完成,那么您必须使用
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);
}
如果你想在全局上下文中执行此操作,你可能无法等待
Promise.all
(旧版浏览器、Node 等只能在 await
函数内 async
),并且你必须使用Promise 提供的较旧的 then
:
Promise.all(
data.map(async(...) => { ... })
).then(() => {
console.log(data)
});
您所需要做的就是使用
Promise.all
和 Array.map
。它将等待所有的承诺完成。见下文。
const newData = await Promise.all(data.map(async(e, i) => {
let req = `https://api.darksky.net/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);
forEach
方法可以进行多个函数调用,而无需等待任何内容,无论这些函数内部发生了什么。
函数内部的流程受
await
影响 - 但 forEach
本身则不然。
使用 for-in 循环进行同步远程请求。
async function makeCalls() {
console.log('starting');
for (d in data) {
let req = `https://api.darksky.net/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 实际上是同步的。它采用回调函数作为参数,在本例中是您的 ASYNC 函数。严格来说,所有代码都已执行,但执行时间与您预期的时间不同。没有一个被跳过。
Async/await 只是 Promise 的语法糖。这意味着循环中“await”之后的每一行代码仅在承诺解决时才会执行。
更好的方法是像其他人建议的那样使用promise.All()。
您可以使用 of:
const foo = async () => {
const arr = [1,2,3,4,5,6];
for (let i of arr) {
const response = await // async operations
}
}
foo();