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

Fetch调用response.json()出现body-timeout错误的原因及解决咨询

问题原因分析

咱们先拆解触发这个body-timeout错误的两个核心问题:

  1. 原生Fetch API不支持timeout配置参数
    你在fetch的配置里加了timeout: REQUEST_TIMEOUT_MS,但这个参数完全无效——原生Fetch根本没有这个选项。这意味着你的请求不会自动终止超时请求,当目标API响应极慢或挂起时,请求会一直等待到浏览器/运行时的默认超时(通常远长于你设置的10000ms),最终在读取响应体时触发body-timeout错误。

  2. Response的响应体流只能被读取一次
    你用cloneDeep(response)传递给foo方法,但cloneDeep(比如Lodash的深拷贝)无法正确克隆Fetch的Response对象——它的body是一个只读的ReadableStream,只能被消费一次。当你在主代码后续执行await response.json()时,已经把响应体的流耗尽了,此时foo里再调用await response.json(),要么直接报错,要么会因为等待已关闭的数据流而超时。

解决方案

针对这两个问题,咱们一步步修复:

1. 给Fetch添加真正的超时控制

用AbortController手动实现超时终止,这是原生Fetch推荐的超时方案:

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);

try {
  response = await fetch(requestUrl, {
    method: 'POST',
    headers,
    signal: controller.signal, // 关联终止信号
    body: JSON.stringify({
      id,
      accNumber: acctId,
      invNumber: invId,
      fromDate,
      toDate
    })
  });
} catch (error) {
  if (error.name === 'AbortError') {
    throw new Error('Request timed out while invoking ATOM Invoice API');
  } else {
    throw new Error('Unexpected error occurred while invoking ATOM Invoice API', { cause: error });
  }
} finally {
  clearTimeout(timeoutId); // 清理定时器,避免内存泄漏
}

这样请求超过设定时间后会主动终止,抛出明确的AbortError,不会一直挂起等待。

2. 正确克隆Response以多次读取响应体

不要用cloneDeep,改用Fetch原生的response.clone()方法——它会创建一个带有独立响应体流的全新Response对象,主代码和foo方法可以各自读取:

// 主代码中
const clonedResponse = response.clone();
foo(clonedResponse); // 传递克隆后的Response
const result = await response.json(); // 原Response的流仍可正常读取

foo方法的代码可以保留,但建议优化错误日志的可读性:

async foo(response){
  let result;
  let errorDescription;
  try {
    result = await response.json();
  } catch (error) {
    logger.error(`Failed to parse response body: ${JSON.stringify(error)}`);
    // 这里可以添加 fallback 逻辑,比如返回默认值或标记错误状态
  }
  // --- some more code ---
}

额外优化建议

  • 检查HTTP状态码:当前代码没有处理API返回的4xx/5xx错误,Fetch只有在网络层面失败时才会抛出异常,建议在获取response后添加:
    if (!response.ok) {
      throw new Error(`ATOM Invoice API returned error: ${response.status} ${response.statusText}`);
    }
    
  • 传递原始错误:在抛出自定义错误时,使用{ cause: error }携带原始错误信息,方便后续排查问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:18:56