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

node-fetch批量大数据并行调用API遇问题:分组请求全拒绝与错误捕获

问题描述

我有大量数据需要通过node-fetch发送至API,用Promise.allSettled实现并行发送,但数据量较大时不仅耗时极久,还会出现执行卡顿。

初始代码(小数据量正常,大数据量异常)

const fetch = require('node-fetch');

// ..... 
// 获取API令牌后调用发送数据的函数
await sendToApi(token);
// .....

async function sendToApi(token) {
  // 已定义headers和requestOptions(使用token)
  try {
    const responses = await Promise.allSettled(
      data.map(async (Item) => {
        path = 'my/path/';
        requestOptions.body = JSON.stringify(Item);
        const res = await fetch(path, requestOptions);
        console.log("res.status: ", res.status);
        return res;
      })
    );
    responses.forEach((response) => {
      console.log("response.status: ", response.status);
    });
  } catch (error) {
    console.log("error: ", error);
  }
}

尝试分组发送后的代码(所有Promise均rejected,try/catch无法捕获错误)

// 获取API令牌后调用发送数据的函数
await sendToApi(token);
// .....

async function sendToApi(token) {
  // 已定义headers和requestOptions(使用token)
  // data为原始大数据数组(测试时已截断)
  await sendInGroups(data.slice(0, n), requestOptions);
  await sendInGroups(data.slice(n + 1, 2 * n), requestOptions);
  await sendInGroups(data.slice(2 * n + 1, 3 * n), requestOptions);
  // .... 以此类推
}

对应的sendInGroups函数:

async sendInGroups(data), requestOptions) {
  try {
    console.log("in try");
    const responses = await Promise.allSettled(
      data.map(async (Item) => {
        path = 'my/path';
        requestOptions.body = JSON.stringify(Item);
        const res = await fetch(path, requestOptions);
        console.log("res.status: ", res.status);
        return res;
      })
    );
    responses.forEach((response) => {
      console.log("response.status: ", response.status);
    });
  } catch (error) {
    console.log(error);
  }
 }
}

问题

  1. 基于现有代码结构,如何捕获被拒绝Promise的错误?
  2. 分组发送的逻辑是否错误?或是存在其他遗漏导致请求失败?

解决方案

1. 捕获被拒绝Promise的错误

Promise.allSettled本身不会抛出错误,它会返回每个Promise的状态结果,因此外层try/catch无法捕获内部Promise的失败。要获取错误信息,需遍历返回的responses数组,检查每个结果的状态:

responses.forEach((response, index) => {
  if (response.status === 'fulfilled') {
    console.log(`第${index+1}个请求成功,状态码:`, response.value.status);
  } else {
    console.error(`第${index+1}个请求失败,原因:`, response.reason);
  }
});

另外注意你的sendInGroups函数存在语法错误:参数列表括号写错,正确写法是async function sendInGroups(data, requestOptions),这个语法错误是导致Promise全部rejected的直接原因之一。

2. 分组发送的问题排查与修正

核心问题点

  • slice用法错误:data.slice(n + 1, 2 * n)会跳过索引为n的元素,正确分组应为data.slice(i * n, (i + 1) * n),确保数据无遗漏。
  • 请求选项竞态问题:多个异步函数同时修改同一个requestOptions对象的body属性,会导致所有请求最终使用最后一个Item的序列化结果,必须为每个请求创建独立的配置对象。
  • 未处理HTTP非成功状态:即使fetch返回响应,若状态码非2xx,也应视为失败并抛出错误,否则Promise.allSettled会将其标记为成功。

修正后的分组发送代码

async function sendToApi(token) {
  // 定义固定的基础请求头
  const baseHeaders = {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
    // 其他固定headers...
  };
  const batchSize = 10; // 根据API并发限制调整此值

  // 自动循环分组,替代手动写多个await
  for (let i = 0; i < data.length; i += batchSize) {
    const batch = data.slice(i, i + batchSize);
    await sendInGroups(batch, baseHeaders);
    // 可选:每组发送后添加短暂延迟,避免触发API限流
    await new Promise(resolve => setTimeout(resolve, 500));
  }
}

async function sendInGroups(data, baseHeaders) {
  try {
    const responses = await Promise.allSettled(
      data.map(async (item) => {
        const path = 'my/path/';
        // 为每个请求创建独立的配置对象
        const requestOptions = {
          method: 'POST', // 根据实际请求方法调整
          headers: {...baseHeaders}, // 复制基础headers,避免引用共享
          body: JSON.stringify(item)
        };
        const res = await fetch(path, requestOptions);
        // 处理HTTP非成功状态
        if (!res.ok) {
          throw new Error(`请求失败:${res.status},响应内容:${await res.text()}`);
        }
        return res;
      })
    );

    // 遍历输出所有请求结果
    responses.forEach((response, index) => {
      if (response.status === 'fulfilled') {
        console.log(`批次内第${index+1}个请求成功,状态码:`, response.value.status);
      } else {
        console.error(`批次内第${index+1}个请求失败:`, response.reason);
      }
    });
  } catch (error) {
    console.error("分组发送时出现全局错误:", error);
  }
}

关键优化说明

  • 每个请求使用独立的requestOptions,彻底解决竞态问题。
  • 自动循环分组,代码更易维护,避免手动切片的遗漏。
  • 显式处理HTTP非2xx状态码,让失败请求能被Promise.allSettled正确捕获。
  • 可选的组间延迟,降低触发API限流的风险。

内容的提问来源于stack exchange,提问作者m-alptekin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 02:57:12