Node 的 fetch 别让请求无限等,给 signal 加个超时

8 次浏览2 条回复

遇到对端一直不返回时,fetch 默认不会替业务代码设超时。Node 20+ 可以直接用 AbortSignal.timeout:

const url = "https://example.com/";

try {
  const response = await fetch(url, {
    signal: AbortSignal.timeout(3000),
  });

  // fetch 遇到 4xx/5xx 不会自动 reject,状态码还是要自己判断
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  console.log(await response.text());
} catch (err) {
  if (err?.name === "TimeoutError") {
    console.error("请求超时");
  } else {
    console.error("请求失败:", err?.message);
  }
}

环境:Node.js 20+,内置 fetch。超时值按接口特点调,别把网络超时和服务端返回的 HTTP 错误混在一起处理。

如果上层还需要“手动取消”,可以把两个 signal 合并,避免只靠超时:

const controller = new AbortController();
const signal = AbortSignal.any([
  controller.signal,
  AbortSignal.timeout(3000),
]);

const response = await fetch(url, { signal });

这样用户取消和超时都能中断请求。处理异常时最好结合 signal.reason 判断原因,别把手动取消也报成超时。AbortSignal.any 适用于 Node.js 20.3+(或相应的较新版本)。

别熬夜啦Lv1#1

如果上层还需要“手动取消”,可以把两个 signal 合并,避免只靠超时:

const controller = new AbortController();
const signal = AbortSignal.any([
  controller.signal,
  AbortSignal.timeout(3000),
]);

const response = await fetch(url, { signal });

这样用户取消和超时都能中断请求。处理异常时最好结合 signal.reason 判断原因,别把手动取消也报成超时。AbortSignal.any 适用于 Node.js 20.3+(或相应的较新版本)。

还有个容易漏的点:AbortSignal.timeout() 生成的是一次性的 signal。做重试时要在每次尝试里重新创建,不能复用已经触发过超时的那个,否则下一次请求会立刻被中止。手动取消的 AbortController 也一样,取消后应换新的 controller。