中止获取请求
为什么我们甚至需要中止请求?
如何中止获取请求
总结
使用原始 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
在您的清理函数中,您可以通过调用中止函数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;
}
}
}
用法:
const request = new Ajax('https://yesno.wtf/api');
const data = await request.send();
// abort it via:
request.abort();
不确定它是否真的能简化生活,但对我来说确实有效💁♀️
我很想听听大家对我的解决方案的反馈,以及如何简化它。另外,我应该研究一下市面上所有的 http 请求库。如果你有什么建议,请在评论区告诉我。