fetch 封装

fetch封装

  1. fetch基本请求
  2. 接口超时 通过AbortController来实现
import type { service } from "./../../api/config";
import { config } from "@/api/config";
import { handleCode } from "./code";
import { formatToken, getToken } from "../auth";
import { ElMessage } from "element-plus";

export type ResponseData<T> = {
  code: number;
  data: T;
  message?: string;
};

type Base = keyof service;

export interface RequestOptions extends Partial<RequestInit> {
  formData?: FormData;
  isBlob?: boolean;
  timeout?: number;
}

export function getOptions(options?: RequestOptions): RequestInit {
  const token = getToken();
  const headers: Record<string, string> = {
    authorization: token ? formatToken(token) : ""
  };
  if (options?.formData) {
    options.body = options.formData;
  } else {
    headers["Content-Type"] = "application/json";
  }
  // 设置默认值
  const defaultOptions: RequestInit = {
    method: "GET",
    headers
  };
  return { ...defaultOptions, ...options };
}

function timeoutPromise(timeout: number, signal: AbortSignal): Promise<never> {
  return new Promise((_, reject) => {
    // 使用 AbortSignal 的 addEventListener 来处理超时
    const timeoutId = setTimeout(() => {
      reject(new Error(`请求超时 ${timeout}ms`));
    }, timeout);

    // 当请求被中断时,清除定时器
    signal.addEventListener(
      "abort",
      () => {
        clearTimeout(timeoutId);
      },
      { once: true }
    ); // 使用 once: true 确保监听器在触发后自动移除
  });
}

const defaultTimeout = 15 * 1000;

export default async <T>(
  url: string,
  baseUrl: Base = "MOCK",
  option?: RequestOptions
): Promise<ResponseData<T>> => {
  const controller = new AbortController();
  const newOption = getOptions({
    ...option,
    signal: controller.signal
  });
  const newUrl = config[baseUrl] + url;

  try {
    const fetchPromise = fetch(newUrl, newOption);
    const timeout = option?.timeout ?? defaultTimeout;
    const response = await Promise.race([
      fetchPromise,
      timeoutPromise(timeout, controller.signal)
    ]);

    let res;
    if (option?.isBlob) {
      res = await response.blob();
    } else {
      res = await response.json();
    }
    if (res.code !== 0 && !option?.isBlob) {
      handleCode(res);
    }
    return res;
  } catch (error) {
    controller.abort(); // 这会同时取消请求和清除定时器
    if (error.message?.includes("请求超时")) {
      ElMessage.error(error.message);
    } else {
      ElMessage.error("服务繁忙,请稍后再试");
    }
    return Promise.reject(error);
  }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值