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

Promise分组串行执行异常:爬虫任务未按组分批启动

问题根源

你当前代码的核心问题是:用links.map(async ...)生成Promise数组时,所有爬虫任务已经立刻启动执行了。

因为async函数只要被调用,就会立即触发内部的异步逻辑(比如await page.browser().newPage()),所以不管你后续怎么分组、怎么调用await Promise.all,所有爬虫任务在map阶段就已经全部并行跑起来了,完全达不到"组间串行"的效果。

解决方案

要实现分批串行+组内并行,核心是延迟Promise的创建与执行——只在处理当前批次时,才为该批次的链接生成Promise并启动任务。

方案1:直接分批遍历链接(最简洁)

不需要额外的拆分函数,直接在循环里按批次处理链接:

async function crawler(links: string[], page: Page): Promise<MyData[]> {
  const list: MyData[] = [];
  const batchSize = 3; // 每组最大并行数

  // 按批次遍历链接
  for (let i = 0; i < links.length; i += batchSize) {
    // 取出当前批次的链接
    const currentBatchLinks = links.slice(i, i + batchSize);
    // 为当前批次的链接创建并启动爬虫任务(此时才真正开始执行)
    const batchPromises = currentBatchLinks.map(async (link) => {
      try {
        const newPage = await page.browser().newPage();
        const detail = await crawlLink(link, newPage);
        await newPage.close();
        return detail;
      } catch (e) {
        console.log(e);
        return null as MyData;
      }
    });
    // 等待当前批次所有任务完成,再进入下一批
    const batchResults = await Promise.all(batchPromises);
    // 过滤无效结果并收集
    const validResults = batchResults.filter(detail => detail !== null) as MyData[];
    list.push(...validResults);
  }

  return list;
}

方案2:复用拆分逻辑(拆分链接数组而非Promise数组)

如果你想保留拆分逻辑,需要修改拆分函数的处理对象——拆分原始链接数组,而非提前生成的Promise数组:

// 修改拆分函数,处理普通数组而非Promise数组
export function splitArray<T>(arr: T[], maxPerItem: number): T[][] {
  const splitList: T[][] = [];
  let currentSplit: T[] = [];

  for (let i = 0; i < arr.length; i++) {
    currentSplit.push(arr[i]);
    if (currentSplit.length === maxPerItem || i === arr.length - 1) {
      splitList.push(currentSplit);
      currentSplit = [];
    }
  }
  return splitList;
}

// 修改爬虫函数
async function crawler(links: string[], page: Page): Promise<MyData[]> {
  const list: MyData[] = [];
  // 拆分链接数组为批次
  const groupedLinks = splitArray(links, 3);

  for (const linkGroup of groupedLinks) {
    // 为当前批次链接创建并启动任务
    const batchPromises = linkGroup.map(async (link) => {
      try {
        const newPage = await page.browser().newPage();
        const detail = await crawlLink(link, newPage);
        await newPage.close();
        return detail;
      } catch (e) {
        console.log(e);
        return null as MyData;
      }
    });
    // 等待当前批次完成
    const batchResults = await Promise.all(batchPromises);
    const validResults = batchResults.filter(detail => detail !== null) as MyData[];
    list.push(...validResults);
  }

  return list;
}
关键总结
  • 不要提前生成所有Promise,async函数一调用就会启动异步任务,这是你之前代码失效的核心原因
  • 正确的逻辑是:先拆分任务单元(链接),再逐批处理,每批内部用Promise.all并行执行,批与批之间用await保证串行

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 01:08:23