Next.js接口中Promise.all请求停滞及forEach返回空对象的问题
解决Next.js GitHub API端点的Promise等待问题
问题描述
在Next.js的API端点中调用GitHub API获取仓库及贡献者数据时遇到两个问题:
- 使用
Promise.all批量请求贡献者数据时,API请求停滞无响应,但控制台能看到publicPromises数组已填充数据 - 改用
forEach循环调用getContributors时,返回的数组元素数量正确,但所有对象都是空的
问题原因分析
Promise.all停滞问题:
getContributors函数中,当GitHub API返回非200状态码时,仅打印日志但未resolve或reject Promise,导致Promise一直处于pending状态,Promise.all会等待所有Promise完成,因此整个请求停滞- 贡献者总数计算逻辑错误:
getContributors返回的是单个对象,而非数组,不需要通过repoInfo[0].contributors取值
forEach返回空数组问题:
forEach不会返回新数组,且内部异步函数不会被等待,因此publicPromises会是undefined,而非预期的结果数组;需用map生成Promise数组后配合await Promise.all实现异步等待
修复方案
1. 修复getContributors函数
确保所有分支都正确resolve或reject Promise,同时移除不必要的全局数组repoInfo(避免并发请求时的竞态问题):
function getContributors(owner, repo, accessToken, type) { return new Promise((resolve, reject) => { const options = { hostname: 'api.github.com', path: `/repos/${owner}/${repo}/contributors`, headers: { 'User-Agent': 'pixls.io', 'Authorization': `token ${accessToken}` } }; const req = https.request(options, res => { let data = ''; res.on('data', chunk => { data += chunk; }); res.on('end', () => { if (res.statusCode && res.statusCode !== 200) { console.log('res.statusCode', res.statusCode); console.log("Error...contributors...."); // 非200状态码时reject终止请求,或resolve默认值(根据需求选择) reject(new Error(`GitHub API returned status ${res.statusCode}`)); // 若需忽略错误继续执行,可替换为: // resolve({ name: repo, privacy: type, contributors: 0 }); } else { try { const contributors = JSON.parse(data); console.log('contributors', contributors.length); resolve({ name: repo, privacy: type, contributors: contributors.length }); } catch (error) { console.log("Error...skipping 2...."); reject(error); } } }); }); req.on('error', error => { console.error(`Error making GitHub API request: ${error}`); reject(error); }); req.end(); }); }
2. 修复API端点逻辑
修正Promise.all的使用和贡献者总数的计算,确保正确等待所有异步请求完成:
try { const { public: publicRepositories, private: privateRepositories, allRepos: repositories } = await getRepositories(accessToken); // 生成公共仓库的贡献者Promise数组,并传入type参数 const publicPromises = await Promise.all( publicRepositories.map(repo => getContributors(repo.owner.login, repo.name, accessToken, 'public') ) ); console.log('publicPromise: ', publicPromises); // 生成私有仓库的贡献者Promise数组,并传入type参数 const privatePromises = await Promise.all( privateRepositories.map(repo => getContributors(repo.owner.login, repo.name, accessToken, 'private') ) ); // 修正总数计算:直接访问对象的contributors属性 const totalPublicContributions = publicPromises.reduce((sum, repoInfo) => sum + repoInfo.contributors, 0); const totalPrivateContributions = privatePromises.reduce((sum, repoInfo) => sum + repoInfo.contributors, 0); const totalContributions = totalPublicContributions + totalPrivateContributions; const accountName = publicRepositories.length > 0 ? publicRepositories[0].owner.login : privateRepositories[0].owner.login; res.status(200).json({ accountName, privateRepoCount: privateRepositories.length, publicRepoCount: publicRepositories.length, totalContributions, publicPromises, privatePromises }); } catch (err) { console.error(err); res.status(500).send('An error occurred while fetching the repositories'); }
额外注意事项
- 若担心GitHub API请求频率限制,可添加请求间隔(如用
setTimeout包装Promise),或改用GitHub GraphQL API减少请求次数 - 处理错误时,可根据业务需求选择终止所有请求,或忽略异常仓库继续执行
内容的提问来源于stack exchange,提问作者saud00
相关产品推荐
相关产品推荐

