Node.js循环执行Promise获取数组数据跨文件导出异常问题
问题根源
你现在的写法本质是用同步逻辑处理异步任务:
- 模块加载时会立刻执行所有同步代码,包括你最后写的
const ArrSong = arr_song赋值、getting_data()的调用 axios.get是异步Promise任务,会被放到事件队列里等所有同步代码执行完才会运行,等接口返回数据往数组里push的时候,你早就把空数组导出给index.js用了- 另外你现在循环里直接调用
getYTData没有等待请求完成,就算等异步执行完,不同接口返回速度不一样,push顺序和歌曲ID顺序不匹配,会出现歌曲和播放量对应错位的问题
修复方案
不要用全局空数组承接异步结果,把整个数据获取逻辑封装成返回Promise的函数,等所有接口请求全部完成后,再把组装好的完整数据返回,调用方等Promise resolve之后再使用数据。
改造后的数据模块代码
const axios = require("axios").default; const YT_API_BASE = "https://youtube.googleapis.com/youtube/v3/videos?part=statistics&id="; const YT_API_KEY = "&key=AIzaSyDwUGeRKMTCeslgQjETBgP1ozqlB0yX9s0"; // 单个歌曲数据查询,直接返回结构化结果,不操作全局变量 async function getYTData(songId) { const reqUrl = `${YT_API_BASE}${songId}${YT_API_KEY}`; const res = await axios.get(reqUrl); // 做一层兜底,避免接口返回异常导致代码报错 const viewCount = res.data.items?.[0]?.statistics?.viewCount ?? 0; return { songId, viewCount }; } // 聚合所有数据的方法,返回Promise,所有请求完成后才会返回结果 async function fetchHotSongData() { // 拉取本地专辑列表 const albumRes = await axios.get("http://localhost:8000/albums/"); const albumList = albumRes.data; // 收集所有需要查询的歌曲ID const allSongIds = []; albumList.forEach(album => allSongIds.push(...album.songs_id)); // 并发请求所有歌曲数据,Promise.all会严格按照传入顺序返回结果,不会错位 // 注意:如果歌曲量超过50首,建议分批请求,YouTube API单次最多支持查询50个视频ID,避免触发限流 const songDataList = await Promise.all( allSongIds.map(songId => getYTData(songId)) ); // 按你需要的格式拆分成两个数组 const arr_song = songDataList.map(item => item.songId); const arr_views = songDataList.map(item => item.viewCount); return { arr_song, arr_views }; } // 导出Promise,不要直接导出空数组 module.exports = fetchHotSongData();
入口文件index.js用法
拿到导出的Promise之后,等数据加载完成再初始化热门歌曲模块:
const songDataPromise = require('./你的数据模块存放路径'); async function initSite() { // 等待所有数据拉取完成 const { arr_song, arr_views } = await songDataPromise; // 拿到有效数据后再渲染热门歌曲专区 renderHotSection(arr_song, arr_views); } initSite();
额外注意点
- 不要在模块顶层直接导出异步操作的中间变量,模块加载的同步阶段拿不到异步任务的执行结果
- 并发请求数如果太高,建议加并发控制,比如同时最多发3-5个请求,避免被YouTube API判定为异常流量封禁key
- 接口请求一定要加异常捕获,可以在
axios.get外面套try/catch,避免单个接口报错导致整个模块崩溃
内容的提问来源于stack exchange,提问作者Yaksh Mahawer
相关产品推荐
相关产品推荐

