前端圈

分享与交流前端开发相关知识

中止fetch请求

使用香草JavaScript fetch()API时,中止请求不太直观。

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

我需要一个可中止的请求的一个特定用例是在React组件内部。组件在安装时获取一些数据,并将获取的数据设置在组件的内部状态内。

由于获取是异步操作,因此可以在解析获取请求之前先卸载组件。因此,如果您正在使用useEffectReact组件中的挂钩,则必须提供一个中止请求的清除功能。

如何中止获取请求

AbortController在获取请求中创建一个并排,并在获取选项中将其作为信号传递:

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

然后,您可以在清理函数中通过调用中止函数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 alongisde 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;
    }
  }
}

用法:

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

// abort it via:
request.abort();

不确定它是否真的使生活更轻松,但是它对我有用。
我很想听听有关我的解决方案以及如何简化此过程的反馈。另外,我应该看看所有这些http请求库。如果您有任何建议,请在评论中告诉我。

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注