使用 Async/Await 获取数据时遇到问题
Trouble fetching data with 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()
循环完成。
在 Whosebug 的某个地方,我看到 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
在任何等待的承诺解决之前完成.
而是使用普通的 for
循环:
for (let alphaCode of fetchedData.borders) {
现在,该循环内的 await
是顶层 async
函数的一部分,因此它按您的预期工作。
如果可以接受创建承诺而不等待前一个承诺解决,您也可以考虑使用 Promise.all
。然后就可以等待Promise.all
的结果了。在您尝试这样做时,您没有将任何内容传递给 Promise.all
,因为 forEach
总是 returns 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
迭代也是同步完成的,但它是 returns 一组承诺,这正是 Promise.all
所需要的。等待发生在 Promise.all
.
之前的 await
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 returns)你可以传递给 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
您好,我正在尝试获取一个国家/地区的数据,然后我想获取其邻国的名称。
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()
循环完成。
在 Whosebug 的某个地方,我看到 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
在任何等待的承诺解决之前完成.
而是使用普通的 for
循环:
for (let alphaCode of fetchedData.borders) {
现在,该循环内的 await
是顶层 async
函数的一部分,因此它按您的预期工作。
如果可以接受创建承诺而不等待前一个承诺解决,您也可以考虑使用 Promise.all
。然后就可以等待Promise.all
的结果了。在您尝试这样做时,您没有将任何内容传递给 Promise.all
,因为 forEach
总是 returns 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
迭代也是同步完成的,但它是 returns 一组承诺,这正是 Promise.all
所需要的。等待发生在 Promise.all
.
await
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 returns)你可以传递给 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