我如何在地图函数中异步和增量地获取数据
How can i asynchronously and incrementally fetch data inside a map function
我正在尝试从数组中逐步获取剧集,因此用户体验如下所示:
用户点击一个按钮,显示一个加载指示器,加载在 div 中获取的第一集,但一直在第一集下方显示加载指示器,一直这样做直到地图结束为止剧集已提取,加载指示器不再显示。
但显然,这似乎没有按计划工作,我怀疑 async-await 在地图中没有按预期工作。
我想知道像 rxjs
这样的东西在这个用例中是否有帮助(我不知道它是如何工作的,但我知道它用于数据流式传输的情况)
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const [data, setData] = useState([]);
const handleClick = async () => {
try {
season.episodes.map(async (episode) => {
setIsLoading(true);
const links = await publicQueryClientApi(
`${process.env.NEXT_PUBLIC_SERVER_URL}/download/${episode.slug}`
);
setIsLoading(false);
setData((prev) => [...prev, { episode, links }]);
setError('');
});
} catch (error) {
setIsLoading(false);
setData([]);
setError(error.message);
}
};
async
函数总是 returns a Promise
.
您可以尝试这样的操作:
const promises = season.episodes.map(async episode => {
const links = await fetchLinks()
// do other stuff
return links
})
const links = await Promise.all(promises)
另一件事是,只有当您想要数组的副本但已修改时才使用 map
(使用传递给 map
的函数,该函数将在每个数组项上执行) .据我所知,map
函数中的 return 值未保存在任何地方。
为什么不只使用递归函数,它会获取每一集,并在有“未获取”的剧集时再次调用自身来获取另一个? (不要忘记每次都将剧集标记为“已提取”,否则会无限循环)。
array.map
不 await
它的回调,如果它是异步的。所以
的结果
[...].map(async x => {})
是一组未决的承诺。如果你想一个一个地添加你的episode
,你应该使用一个
try {
for (let episode of season.episodes) {
setIsLoading(true);
const links = await publicQueryClientApi(
`${process.env.NEXT_PUBLIC_SERVER_URL}/download/${episode.slug}`
);
setIsLoading(false);
setData((prev) => [...prev, { episode, links }]);
setError('');
}
} catch (e) {
...
}
由于您的代码已经在 async
函数中,因此无需任何进一步修改即可运行。
另一种可能性是使用 Promise.all()
,一旦数组中的所有 promise 都被解析并且return它们的结果在数组中
,这将解析
try {
setIsLoading(true);
let allLinks = await Promise.all(season.episodes.map(e => {
return publicQueryClientApi(...);
});
setIsLoading(false);
setData(season.episodes.map((e,i) => ({
episode: e,
links: alllinks[i]
}));
setError('');
} catch (e) {
...
}
我正在尝试从数组中逐步获取剧集,因此用户体验如下所示:
用户点击一个按钮,显示一个加载指示器,加载在 div 中获取的第一集,但一直在第一集下方显示加载指示器,一直这样做直到地图结束为止剧集已提取,加载指示器不再显示。
但显然,这似乎没有按计划工作,我怀疑 async-await 在地图中没有按预期工作。
我想知道像 rxjs
这样的东西在这个用例中是否有帮助(我不知道它是如何工作的,但我知道它用于数据流式传输的情况)
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const [data, setData] = useState([]);
const handleClick = async () => {
try {
season.episodes.map(async (episode) => {
setIsLoading(true);
const links = await publicQueryClientApi(
`${process.env.NEXT_PUBLIC_SERVER_URL}/download/${episode.slug}`
);
setIsLoading(false);
setData((prev) => [...prev, { episode, links }]);
setError('');
});
} catch (error) {
setIsLoading(false);
setData([]);
setError(error.message);
}
};
async
函数总是 returns a Promise
.
您可以尝试这样的操作:
const promises = season.episodes.map(async episode => {
const links = await fetchLinks()
// do other stuff
return links
})
const links = await Promise.all(promises)
另一件事是,只有当您想要数组的副本但已修改时才使用 map
(使用传递给 map
的函数,该函数将在每个数组项上执行) .据我所知,map
函数中的 return 值未保存在任何地方。
为什么不只使用递归函数,它会获取每一集,并在有“未获取”的剧集时再次调用自身来获取另一个? (不要忘记每次都将剧集标记为“已提取”,否则会无限循环)。
array.map
不 await
它的回调,如果它是异步的。所以
[...].map(async x => {})
是一组未决的承诺。如果你想一个一个地添加你的episode
,你应该使用一个
try {
for (let episode of season.episodes) {
setIsLoading(true);
const links = await publicQueryClientApi(
`${process.env.NEXT_PUBLIC_SERVER_URL}/download/${episode.slug}`
);
setIsLoading(false);
setData((prev) => [...prev, { episode, links }]);
setError('');
}
} catch (e) {
...
}
由于您的代码已经在 async
函数中,因此无需任何进一步修改即可运行。
另一种可能性是使用 Promise.all()
,一旦数组中的所有 promise 都被解析并且return它们的结果在数组中
try {
setIsLoading(true);
let allLinks = await Promise.all(season.episodes.map(e => {
return publicQueryClientApi(...);
});
setIsLoading(false);
setData(season.episodes.map((e,i) => ({
episode: e,
links: alllinks[i]
}));
setError('');
} catch (e) {
...
}