需求分析

支持以下两种取消方式

  • 取消方式1 ```typescript const CancelToken = axios.CancelToken; let cancel;

axios.get(‘/user/12345’, { cancelToken: new CancelToken(function executor(c) { cancel = c; }) });

// 取消请求 cancel();

  1. - 取消方式2
  2. ```typescript
  3. const CancelToken = axios.CancelToken;
  4. const source = CancelToken.source();
  5. axios.get('/user/12345', {
  6. cancelToken: source.token
  7. }).catch(function (e) {
  8. if (axios.isCancel(e)) {
  9. console.log('Request canceled', e.message);
  10. } else {
  11. // 处理错误
  12. }
  13. });
  14. // 取消请求 (请求原因是可选的)
  15. 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
  • axios.isCancel

    声明类型

    reson类型从string,修改为Cancel, 为了实现 isCancel通过 instanceOf判断即可 ```typescript export interface AxiosStatic extends AxiosInstance { create(config?: AxiosRequestConfig): AxiosInstance

    CancelToken: CancelTokenStatic Cancel: CancelStatic isCancel: (val: any) => boolean }

export interface CancelToken { promise: Promise reason?: Cancel }

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()
  }
}