You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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]);
}

关键修改点说明:

  1. fetchArtistGenres去掉多余的.then嵌套,直接用await获取结果并返回,逻辑更清晰
  2. 新增fetchSongs异步函数包裹所有请求逻辑,让我们可以用await等待每一步操作完成
  3. 将原map改为async map,每个遍历项返回Promise,再通过Promise.all等待所有Promise完成,确保所有Song对象都在拿到genres后才创建
  4. 把全局的var genres移到遍历项内部声明,避免异步操作中的变量污染

内容的提问来源于stack exchange,提问作者anon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 11:35:34