如何解决使用Async / Await无法获取数据
嗨,我正在尝试获取一个国家/地区的数据,然后再获取其邻近国家/地区的名称。
import React from 'react';
export default function DetailsRoute({ match }) {
const [countryData,setCountryData] = React.useState({});
const [borderCountries,setBorderCountries] = React.useState([]);
React.useEffect(() => {
fetchCountryData();
},[])
const fetchCountryData = async () => {
/* fetch country by Name */
const response = await fetch(`https://restcountries.eu/rest/v2/name/${match.params.country}`);
const fetchedData = (await response.json())[0];
setCountryData(fetchedData);
const neighbors = [];
/* Extract 'alphaCode' for each bordered country and fetch its real name */
fetchedData.borders.forEach(async (alphaCode) => {
const response = await fetch(`https://restcountries.eu/rest/v2/alpha/${alphaCode}`);
const fetchedNeighbor = await response.json();
neighbors.push(fetchedNeighbor.name);
});
/* THIS DOESN'T WAIT FOR NEIGHBORS TO BE FILLED UP */
setBorderCountries(neighbors);
}
return (
<article>
<h1>{countryData.name}</h1>
{borderCountries.map(countryName => <h2>{countryName}</h2>)}
</article>
)
}
如您所见,setBorderCountries(neighbors)
并非异步运行。但是我不知道如何让它等待forEach()
循环结束。
在stackoverflow上的某个地方,我看到了Promise.all()
并试图实现它,但我真的不知道它在语法上是否正确-
Promise.all(
fetchedData.borders.forEach(async (alphaCode) => {
const response = await fetch(`https://restcountries.eu/rest/v2/alpha/${alphaCode}`);
const fetchedNeighbor = await response.json();
neighbors.push(fetchedNeighbor.name);
})
)
.then(() =>
setBorderCountries(neighbors)
)
我的问题是如何让setBorderCountries(neighbors)
等到forEach()
循环完成填充neighbors
为止?
还有可能建议对我的代码进行一些优化?
解决方法
forEach
循环立即完成,因为等待(仅)发生在回调中,但是这些回调都被同步调用,因此forEach
在任何等待的诺言解析之前完成。 / p>
请使用普通的for
循环:
for (let alphaCode of fetchedData.borders) {
现在,该循环内的await
是顶级async
函数的一部分,因此可以按预期工作。
如果可以接受创建诺言而无需等待前一个诺言得到解决,那么您也可以考虑使用Promise.all
。然后,您可以等待Promise.all
的结果。尝试这样做时,您没有传递任何内容给Promise.all
,因为forEach
总是返回undefined
。正确的方法将如下使用.map
:
const neighbors = await Promise.all(
fetchedData.borders.map(async (alphaCode) => {
const response = await fetch(`https://restcountries.eu/rest/v2/alpha/${alphaCode}`);
const fetchedNeighbor = await response.json();
return fetchedNeighbor.name; // return it!!
});
)
setBorderCountries(neighbors);
请注意,.map
迭代在此也是同步完成的,但是它返回一个承诺数组,而这正是Promise.all
所需要的。等待发生在await
之前的Promise.all
。
Promise.all
兑现了一系列承诺。您的代码会将forEach
调用(未定义)的结果传递给Promise.all
,这不会做您想要的事情。
如果您使用map,则可以创建一个请求承诺数组。类似于:
const fetchNeighbor = async (alphaCode) => {
return fetch(`https://restcountries.eu/rest/v2/alpha/${alphaCode}`)
.then(response => response.json())
.then(n => n.name);
}
const neighborNames = await Promise.all(fetchedData.borders.map(fetchNeighbor));
setBorderCountries(neighbors);
Array.map通过在源数组中的每个元素上运行给定函数来生成新数组。因此,这两个大致相同:
const promises = fetchedData.borders.map(fetchNeighbor);
const promises = [];
fetchedData.borders.forEach(alphaCode => {
promises.push(fetchNeighbor(alphaCode));
});
您现在有了一个承诺数组(因为那是fetchNeighbor返回的内容),您可以将其传递给Promise.all:
const results = await Promise.all(promises);
Promise.all使用已解析的承诺值数组进行解析。由于fetchNeighbor
最终使用名称进行解析,因此您现在有了一个名称数组。
const results = await Promise.all(promises);
console.log(results);
// ['Country A','Country B','Country C',... ]
,
我认为您应该使用这样的东西。
const start = async () => {
await asyncForEach([1,2,3],async (num) => {
await waitFor(50);
console.log(num);
});
console.log('Done');
}
start();
我认为这篇文章是学习的好资源:async/await with for each
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。