Skip to content

错误、超时与取消 ​

Fetcher 默认管线拒绝 200–299 之外的 HTTP 状态。原生 fetch 本身会正常返回这些响应,因此处理 Fetcher 请求失败时应检查 exchange。

复用 HTTP 状态接受策略时选择 validateStatus,明确只跳过一次校验时才使用请求属性,由调用方取消时使用 AbortSignal。这些入口都不会安排重试。即使装有错误拦截器,成功响应后的 JSON 解析失败仍需调用方捕获。

错误类型与状态策略 ​

API契约
FetcherError(message?, cause?)消息回退到 Error cause 的消息,再回退到通用消息;保存 cause。保留自身 stack(失败浮现之处),原始失败及其 stack 留在 cause 上。子类无需 Object.setPrototypeOf 即可通过 instanceof。
ExchangeError(exchange, message?)保存 exchange,cause 取自 exchange.error;消息依次回退到错误消息、响应 statusText、请求 URL。
HttpStatusValidationError(exchange)状态校验创建,包含状态码与 URL;exchange 以它本身拒绝(不包装),.exchange.error 是同一个错误。
FetchTimeoutError(request)保存超时请求,消息包含超时、方法(默认 GET)和 URL。
ValidateStatus(status: number) => boolean,用于构造选项或 new ValidateStatusInterceptor(predicate)。
IGNORE_VALIDATE_STATUS属性键 '__ignoreValidateStatus__',仅字面量 true 绕过校验。

没有响应时状态校验直接跳过。管线失败通常为 ExchangeError:状态失败就是 HttpStatusValidationError 本身,其他错误(超时、网络、abort reason、请求/响应/错误拦截器抛错)被包装,原错误作为 cause。只有后续提取器失败可以不经过此包装。不要假定每个失败都有响应,也不要假定所有拒绝值都是 Error。

超时优先级 ​

TimeoutCapable.timeout?: number 单位为毫秒。resolveTimeout(requestTimeout?, optionsTimeout?) 在请求值不是 undefined 时直接返回它(包括零),否则使用客户端值。timeoutFetch(request, fetchImplementation?): Promise<Response> 使用 fetchImplementation 发送,其类型 FetchImplementation 为 (input: string, init?: RequestInit) => Promise<Response>(默认在调用时读取全局 fetch)。URL 作为第一个参数,并传入干净的 RequestInit:不含 url、timeout、urlParams 和 abortController,取消通过合并后的 signal 传递。行为如下:

输入行为
timeout 不是正数(0、负数、undefined)不启用定时器;原生 fetch 由 request.signal 或 abortController.signal 中先触发者中止。
timeout 为正数原生 fetch 与定时器竞速,定时器与调用者的 signal、abortController 组合,先触发者中止。定时器以 FetchTimeoutError 拒绝,调用者取消以其 abort reason 拒绝。

传入有限正数才启用定时器。fetch 返回 Response 时定时器即结束,不会等待响应正文消费完成。流的空闲/总时限需要调用者管理取消。

所有权与清理 ​

成功和失败都会清理定时器。timeoutFetch 从不写入请求对象,定时器也从不中止外部控制器,因此同一请求可以再次发送。外部控制器仍由调用者拥有,即使它已被中止;它不能复位用于重试,新操作应创建新控制器。

外部取消使用 signal 或 abortController,失败 exchange 保留取消原因。两者都与 Fetcher 超时同时生效:先触发者中止请求。

完整示例 ​

ts
import { Fetcher, ExchangeError, FetchTimeoutError } from '@ahoo-wang/fetcher';

const client = new Fetcher({
  baseURL: 'https://api.example.com',
  timeout: 3000,
});
const controller = new AbortController();
try {
  const response = await client.get('/users/1', {
    abortController: controller,
  });
  console.log(await response.json());
} catch (error) {
  if (error instanceof ExchangeError) {
    if (error.cause instanceof FetchTimeoutError) console.error('Timed out');
    else console.error(error.exchange.response?.status, error.cause);
  } else {
    console.error(error);
  }
}

公开符号与源码 ​

符号实现
FetcherErrorfetcherError.ts:40
ExchangeErrorfetcherError.ts:83
FetchTimeoutErrortimeout.ts:35
TimeoutCapabletimeout.ts:60
resolveTimeouttimeout.ts:81
FetchImplementationtimeout.ts:132
timeoutFetchtimeout.ts:184
HttpStatusValidationErrorvalidateStatusInterceptor.ts:27
ValidateStatusvalidateStatusInterceptor.ts:61
VALIDATE_STATUS_INTERCEPTOR_NAMEvalidateStatusInterceptor.ts:69
VALIDATE_STATUS_INTERCEPTOR_ORDERvalidateStatusInterceptor.ts:76
IGNORE_VALIDATE_STATUSvalidateStatusInterceptor.ts:96
ValidateStatusInterceptorvalidateStatusInterceptor.ts:125

包索引

基于 Apache License 2.0 发布。