需求分析
支持以下两种取消方式
- 取消方式1 ```typescript const CancelToken = axios.CancelToken; let cancel;
axios.get(‘/user/12345’, { cancelToken: new CancelToken(function executor(c) { cancel = c; }) });
// 取消请求 cancel();
- 取消方式2```typescriptconst CancelToken = axios.CancelToken;const source = CancelToken.source();axios.get('/user/12345', {cancelToken: source.token}).catch(function (e) {if (axios.isCancel(e)) {console.log('Request canceled', e.message);} else {// 处理错误}});// 取消请求 (请求原因是可选的)source.cancel('Operation canceled by the user.');
实现取消方式1
核心思路,在请求配置传入axiosToken, 该实例包含一个promise, 在初始化时传入 cancelExecutor,该函数参数是一个函数,调用时将padding状态的promise变成resolved。
xhr 发送请求前,将axiosToken从配置中取出,在axiosToken.promise.then调用取消逻辑
声明AxiosToken类型
export interface AxiosRequestConfig {
url?: string
method?: Method
data?: any
params?: any
headers?: any
responseType?: XMLHttpRequestResponseType
timeout?: number
transformRequest?: AxiosTransformer | AxiosTransformer[]
transformResponse?: AxiosTransformer | AxiosTransformer[]
cancelToken?: CancelToken // <====
[key: string]: any
}
export interface CancelToken {
promise: Promise<string>
reson?: string
}
export interface Canceler {
(message: string): void
}
export interface CancelExecutor {
(cancel: Canceler): void
}
定义AxiosToken class
import { CancelExecutor } from "../types"
interface PromiseResolve {
(reason?: string): void
}
export class CancelToken {
promise: Promise<string | undefined>
reason?: string
constructor(executor: CancelExecutor) {
let promiseResolve: PromiseResolve
this.promise = new Promise<string | undefined>((resolve) => {
promiseResolve = resolve
})
// 执行传入的executor
executor(message => {
if (this.reason) {
return
}
this.reason = message
promiseResolve(message)
})
}
}
执行取消
从配置中取出axiosToken, 并添加取消逻辑
const { cancelToken } = config
if (cancelToken) {
cancelToken.promise.then(message => {
request.abort()
reject(message)
})
}
实现取消方式2
在方式1的基础上,通过一个静态方法souce, 把 new CancelToken 和赋值cencel的逻辑放在souce静态方法中,并返回。
声明类型
export interface CancelTokenSource {
token: CancelToken
cancel: Canceler
}
export interface CancelTokenStatic {
new(executor: CancelExecutor): CancelToken
source(): CancelTokenSource
}
实现souce静态方法
import { Canceler, CancelExecutor, CancelTokenSource } from "../types"
interface PromiseResolve {
(reason?: string): void
}
export class CancelToken {
promise: Promise<string | undefined>
reason?: string
constructor(executor: CancelExecutor) {
let promiseResolve: PromiseResolve
this.promise = new Promise<string | undefined>((resolve) => {
promiseResolve = resolve
})
// 执行传入的executor
executor(message => {
if (this.reason) {
return
}
this.reason = message
promiseResolve(message)
})
}
// 相当于CancelToken的工厂函数,把token和cancel返回
static source(): CancelTokenSource {
let cancel: Canceler
const token:CancelToken = new CancelToken((c) => {
cancel = c
})
return {
cancel: cancel!,
token
}
}
}
扩展axios方法
- axios.CancelToken
- axios.Cancel
-
声明类型
reson类型从string,修改为
Cancel, 为了实现isCancel通过instanceOf判断即可 ```typescript export interface AxiosStatic extends AxiosInstance { create(config?: AxiosRequestConfig): AxiosInstanceCancelToken: CancelTokenStatic Cancel: CancelStatic isCancel: (val: any) => boolean }
export interface CancelToken {
promise: Promise
export interface Canceler { (message: string): void } export interface CancelExecutor { (cancel: Canceler): void }
export interface CancelTokenSource { token: CancelToken cancel: Canceler }
export interface CancelTokenStatic { new(executor: CancelExecutor): CancelToken source(): CancelTokenSource }
export interface Cancel { message?: string } export interface CancelStatic { new(message?: string): Cancel }
<a name="j1HfF"></a>
### 修改 AxiosToken
对应以上类型声明,把reason修改为Reason
```typescript
import { Canceler, CancelExecutor, CancelTokenSource } from "../types"
import Cancel from "./Cancel"
interface PromiseResolve {
(reason: Cancel): void
}
export class CancelToken {
promise: Promise<Cancel>
reason?: Cancel
constructor(executor: CancelExecutor) {
let promiseResolve: PromiseResolve
this.promise = new Promise<Cancel>((resolve) => {
promiseResolve = resolve
})
// 执行传入的executor
executor(message => {
if (this.reason) {
return
}
this.reason = new Cancel(message)
promiseResolve(this.reason)
})
}
// 相当于CancelToken的工厂函数,把token和cancel返回
static source(): CancelTokenSource {
let cancel: Canceler
const token = new CancelToken((c) => {
cancel = c
})
return {
cancel: cancel!,
token
}
}
}
新增Cancel
export default class Cancel {
message?: string
constructor(message?: string) {
this.message = message
}
}
export function isCancel(val: any): boolean {
return val instanceof Cancel
}
优化
如果已经调用cancel, 使用到该token的请求不再发送请求。
修改类型声明
export interface CancelToken {
promise: Promise<Cancel>
reason?: Cancel
throwIfCanceled: () => void
}
新增throwIfCanceled
throwIfCanceled() {
if (this.reason) {
throw this.reason
}
}
在请求前调用
export default function dispatchRequest(config: AxiosRequestConfig): AxiosPromise {
throwIfCanceledBeforeRequest(config)
return xhr(processConfig(config)).then(response => {
return transformResponseData(response)
})
}
function throwIfCanceledBeforeRequest(config: AxiosRequestConfig) {
if (config.cancelToken) {
config.cancelToken.throwIfCanceled()
}
}
