Next.js ISR构建时如何设置页面间延迟以规避API请求限制?
解决Next.js ISR构建时第三方API限流问题
一、核心解决方案
1. 全局请求限流队列
单个页面的延迟控制没用,因为Next.js构建时会并发执行多个getStaticProps,必须做全局的请求流量控制。可以实现一个限流类,把所有API请求放到队列里,严格控制每秒请求数:
// 全局限流工具类 class RateLimiter { constructor(requestsPerSecond) { this.queue = []; this.requestInterval = 1000 / requestsPerSecond; this.lastRequestTime = 0; this.isProcessing = false; } async runRequest(fn) { return new Promise((resolve) => { this.queue.push({ fn, resolve }); this.processQueue(); }); } async processQueue() { if (this.isProcessing || this.queue.length === 0) return; this.isProcessing = true; // 计算距离上次请求的时间差,确保间隔符合限流要求 const now = Date.now(); const timeToWait = this.requestInterval - (now - this.lastRequestTime); if (timeToWait > 0) { await new Promise(resolve => setTimeout(resolve, timeToWait)); } // 执行队列中的请求 const { fn, resolve } = this.queue.shift(); this.lastRequestTime = Date.now(); try { const result = await fn(); resolve(result); } catch (err) { resolve(Promise.reject(err)); } finally { this.isProcessing = false; this.processQueue(); } } } // 实例化,对应每秒15次请求的限制 const apiLimiter = new RateLimiter(15); // 在getStaticProps中使用 export async function getStaticProps({ params }) { const data = await apiLimiter.runRequest(() => fetch(`https://第三方API地址/${params.id}`).then(res => res.json()) ); return { props: { data }, revalidate: 86400 // ISR刷新间隔 }; }
2. 限制Next.js构建并发数
Next.js默认会用多线程并发构建页面,你可以强制串行构建,配合限流逻辑进一步降低风险。在next.config.js中配置:
module.exports = { experimental: { workerThreads: false, cpus: 1 // 强制单线程构建 } };
这种方式会拉长构建时间,但适合页面数量不多的场景。
3. 预缓存API数据到本地
提前通过单独脚本拉取所有需要的API数据(用限流逻辑),存到本地文件或数据库,构建时直接读取本地数据,彻底避开构建时的API请求:
// prefetch-data.js 预拉取脚本 const fs = require('fs'); const RateLimiter = require('./RateLimiter'); const apiLimiter = new RateLimiter(15); async function fetchAllData() { const pageIds = ['1', '2', '3', ...]; // 所有需要生成页面的ID列表 for (const id of pageIds) { const data = await apiLimiter.runRequest(() => fetch(`https://第三方API地址/${id}`).then(res => res.json()) ); fs.writeFileSync(`./data/${id}.json`, JSON.stringify(data)); } } fetchAllData();
然后在getStaticProps中读取本地文件:
import fs from 'fs/promises'; import path from 'path'; export async function getStaticProps({ params }) { const filePath = path.join(process.cwd(), 'data', `${params.id}.json`); const data = JSON.parse(await fs.readFile(filePath, 'utf8')); return { props: { data }, revalidate: 86400 }; }
二、单个Promise+Timeout无效的原因
Next.js构建静态页面时,会并发执行多个页面的getStaticProps,每个getStaticProps都是独立的执行上下文。你在单个请求前加的setTimeout,只会延迟当前页面的请求,但其他页面的请求依然会同时发起,全局QPS还是会超过API限制。
比如同时有20个页面在构建,每个都加100ms延迟,100ms后20个请求会同时发送,直接触发API限流。
三、额外注意事项
- 优先检查API是否支持批量请求:如果允许一次请求获取多条数据,直接改成批量请求能大幅减少请求次数,从根源解决问题。
- 加重试逻辑:限流过程中可能遇到API返回429错误,要在限流函数中添加重试机制,避免构建失败。
- ISR刷新时的限流:ISR后台刷新页面时也会触发
getStaticProps,所以限流逻辑要覆盖这个场景。
内容的提问来源于stack exchange,提问作者Rashad AL-Maqtary
相关产品推荐
相关产品推荐

