中止获取请求 为什么我们需要中止请求? 如何中止获取请求 总结

2025-06-07

中止获取请求

为什么我们甚至需要中止请求?

如何中止获取请求

总结

使用原始 JavaScript fetch()API 时,中止请求并不是太直观。

为什么我们甚至需要中止请求?

我需要一个可中止请求的具体用例是在 React 组件内部。该组件在挂载时获取一些数据,并将获取的数据设置到组件的内部状态中。

由于获取数据是异步操作,组件可能在获取数据请求解析之前就被卸载。因此,如果你在 React 组件中使用钩子useEffect,则必须提供一个中止请求的清理函数。

如何中止获取请求

创建一个AbortController与您的获取请求一起的并在获取选项中传递其信号属性:

const { signal } = new AbortController();
const response = await fetch('https://yesno.wtf/api', {signal});
const data = await response.json();
// do something with data
Enter fullscreen mode Exit fullscreen mode

在您的清理函数中,您可以通过调用中止函数signal.abort();

总结

对于我的项目,我将其全部包装在一个 fetch 包装器类中。在我的项目中,我使用了 TypeScript,并且针对我的具体用例做了一些决定:

因为只需要 JSON 数据,所以我硬编码response.json()了它💁‍♀️。此外,如果响应不是 2xx 左右,我会抛出异常:

/**
 * Exceptions from the API
 */
export interface ApiException {
  status: number;
  details: any; 
}

/**
 * Request State
 */
export enum RequestState {
  IDLE = 'idle',
  ABORTED = 'aborted',
  PENDING = 'pending',
  READY = 'ready',
  ERROR = 'error'
}

/**
 * Ajax class
 * 
 * Wrapper class around the fetch API. 
 * It creates an AbortController alongside with the request.
 * Also, it keeps track of the request state and throws an ApiException on HTTP status code !== 2xx
 * 
 */
export class Ajax<T = any> {

  promise: Promise<Response> | null;
  abortController: AbortController | null;

  info: RequestInfo;
  init: RequestInit;

  state: RequestState;

  /**
   * Ajax constructor. Takes the same arguments as fetch()
   * @param info 
   * @param init 
   */
  constructor(info: RequestInfo, init?: RequestInit) {
    this.abortController = new AbortController();
    this.init = { ...(init || {}), signal: this.abortController.signal };
    this.info = info;
    this.state = RequestState.IDLE;
    this.promise = null;
  }

  /**
   * Send API request. 
   * 
   * @returns {any} json data (await (await fetch()).json())
   * @throws {ApiException} exception if http response status code is not 2xx
   * 
   */
  async send(): Promise<T> {
    this.state = RequestState.PENDING;
    try {
      this.promise = fetch(this.info, this.init);
      const response = await this.promise;
      const json = await response.json();
      if (! response.ok) {
        throw {status: response.status, details: json} as ApiException;
      }
      this.state  = RequestState.READY;
      return json;
    } catch (ex) {
      this.state = RequestState.ERROR;
      throw ex;
    } finally {
      this.abortController = null;
    }
  }

  /**
   * Cancel the request.
   */
  abort(): void {
    if (this.abortController) {
      this.state = RequestState.ABORTED;
      this.abortController.abort();
      this.abortController = null;
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

用法:

const request = new Ajax('https://yesno.wtf/api');
const data = await request.send();

// abort it via:
request.abort();
Enter fullscreen mode Exit fullscreen mode

不确定它是否真的能简化生活,但对我来说确实有效💁‍♀️
我很想听听大家对我的解决方案的反馈,以及如何简化它。另外,我应该研究一下市面上所有的 http 请求库。如果你有什么建议,请在评论区告诉我。

文章来源:https://dev.to/learosema/aborting-a-fetch-request-4pmb
PREV
我“喜欢”反模式
NEXT
解决 PostgreSQL com CTE 的性能问题