ReactJS中如何在useEffect中正确等待API调用?
解决React useEffect中嵌套API调用的异步顺序问题
你的问题核心是异步操作未等待完成就执行了后续代码:在map遍历创建Song对象时,调用fetchArtistGenres获取流派的异步请求还没返回结果,就已经执行了创建Song的代码,导致genres还没赋值就被传入构造函数,日志顺序也乱了。
直接按以下步骤修改代码即可解决:
- 把处理歌曲数据的逻辑改成异步函数,因为需要等待每个异步请求完成后再生成
Song对象 - 修复
fetchArtistGenres的返回值(原函数没有返回异步请求的结果) - 使用
Promise.all等待所有歌曲的异步处理完成后,再调用setSongs更新状态
修改后的完整代码:
class Song { constructor(id, title, artists, genres) { this.id = id; this.title = title; this.artists = artists; this.genres = genres; } } export default function Dashboard({ code }) { const accessToken = useAuth(code); const [songs, setSongs] = useState([]); console.log(songs); useEffect(() => { // 修复:让函数返回异步请求的结果 const fetchArtistGenres = async (artistId) => { const res = await spotifyApi.getArtist(artistId); console.log("GENRES FROM FETCH FUNCTION", res.body.genres); return res.body.genres; }; if (accessToken) { // 外层改成async/await,方便处理内部异步逻辑 const fetchSongs = async () => { const savedTracksRes = await spotifyApi.getMySavedTracks({ limit: 2 }); // 用Promise.all等待所有歌曲的异步处理完成 const processedSongs = await Promise.all( savedTracksRes.body.items.map(async (item) => { const artists = item.track.artists.map((artist) => ({ name: artist.name, id: artist.id, })); let genres; if (!item.track.genres) { // 等待流派请求完成再赋值 genres = await fetchArtistGenres(artists[0].id); console.log("GENRES FROM PROMISE", genres); } else { genres = item.track.genres; } console.log("genres before creating song: " + genres); return new Song( item.track.id, item.track.name, artists, genres ); }) ); setSongs(processedSongs); }; fetchSongs().catch(err => console.log(err)); } }, [accessToken]); }
关键修改点说明:
fetchArtistGenres去掉多余的.then嵌套,直接用await获取结果并返回,逻辑更清晰- 新增
fetchSongs异步函数包裹所有请求逻辑,让我们可以用await等待每一步操作完成 - 将原
map改为async map,每个遍历项返回Promise,再通过Promise.all等待所有Promise完成,确保所有Song对象都在拿到genres后才创建 - 把全局的
var genres移到遍历项内部声明,避免异步操作中的变量污染
内容的提问来源于stack exchange,提问作者anon
相关产品推荐
相关产品推荐

