
本文介绍在 JavaScript 中批量处理大量数据时避免阻塞、提升吞吐量的核心策略:通过并发控制 + Promise 并行执行 + 合理延时调度,替代串行 await 循环,显著提升吞吐能力并防止服务过载。
本文介绍在 javascript 中批量处理大量数据时避免阻塞、提升吞吐量的核心策略:通过并发控制 + promise 并行执行 + 合理延时调度,替代串行 `await` 循环,显著提升吞吐能力并防止服务过载。
当面对 100 或 1000 篇新闻文章需逐篇完成「爬取 → AI 处理 → 图像生成 → S3 上传 → Strapi 发布」这一长链路异步操作时,原始代码中 for...of 内 await 的串行执行模式会带来严重瓶颈:不仅总耗时线性增长(如单篇平均 5s,100 篇即 500s+),还极易触发目标服务限流(如 OpenAI API、DALL·E、目标网站反爬)、内存堆积甚至 Node.js 事件循环阻塞。
根本优化思路是:解耦“启动”与“等待”——对每篇文章立即发起处理流程(返回 Promise),而非等上一篇全部完成再开始下一篇;同时引入轻量级节流,避免瞬时并发过高。
✅ 推荐实现:可控并发 + 延迟调度
首先将单篇文章处理逻辑抽离为独立函数,确保职责清晰、便于复用与测试:
const processArticle = async (article) => {
const { title, sourceUrl, id, publishedAt } = article;
console.log(`[Article ${id}] Starting processing...`);
try {
const markup = await scraper(sourceUrl);
const data = await askGpt(markup);
const generatedImageUrl = await generateImg(data?.imageDescription);
const s3ImageUrl = await generateImgUrl(generatedImageUrl, title, id);
await createPost(
data?.title,
data?.abstract,
data?.content,
s3ImageUrl,
publishedAt,
data?.categories
);
console.log(`[Article ${id}] Successfully published to Strapi.`);
} catch (error) {
console.error(`[Article ${id}] Failed:`, error.message);
}
};
接着,在主函数中采用 “发起即推进 + 固定间隔” 模式启动所有任务,并统一等待结果:
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const fetchAndProcessNews = async (queryString, from) => {
const query = { queryString, from, size: 1 };
try {
console.log('Fetching news...');
const { articles } = await searchApi.getNews(query);
console.log(`Fetched ${articles.length} articles.`);
if (!articles?.length) {
console.log('No articles found.');
return;
}
console.log('Initiating concurrent processing...');
const promises = [];
for (const article of articles) {
promises.push(processArticle(article));
await delay(100); // 关键节流:每 100ms 启动一个新任务(可按压测调优)
}
// 等待全部任务结束(成功/失败均计入)
const results = await Promise.allSettled(promises);
const fulfilled = results.filter(r => r.status === 'fulfilled').length;
const rejected = results.filter(r => r.status === 'rejected').length;
console.log(`Processing completed. Success: ${fulfilled}, Failed: ${rejected}`);
} catch (error) {
console.error('Critical error in fetch phase:', error);
}
};
⚠️ 关键注意事项
-
延迟值需实测调整:
100ms是保守起点,实际应结合各下游服务的 QPS 限制(如 OpenAI 免费 tier 限 3 RPM)、网络 RTT 及自身服务器资源压测确定。可逐步降至50ms或升至200ms。 -
错误隔离:使用
Promise.allSettled而非Promise.all,确保单篇文章失败不影响其余处理;日志中明确标注article.id,便于排查。 -
内存友好:相比
Promise.all()一次性拉起全部请求,本方案通过delay控制并发“启动速率”,避免瞬间创建上千个 pending Promise 导致内存飙升。 -
不推荐无节制并发:直接
articles.map(processArticle)后Promise.all(...)虽最简,但极易触发ERR_HTTP2_GOAWAY、ETIMEDOUT或被目标服务封禁,生产环境务必规避。 - 进阶选型建议:若需持久化、重试、优先级或分布式扩展,再引入 BullMQ / RabbitMQ 等专业队列;当前规模下,轻量节流已足够稳健。
该方案在保持代码简洁性的同时,将 100 篇文章的总耗时从 500+ 秒(串行) 降至 约 60–90 秒(并发+节流),吞吐量提升 5–8 倍,且系统稳定性显著增强——这才是面向真实业务场景的可持续工程实践。










