基於 TS 實現 axios(七)

這一章節,主要是寫取消請求

目錄:

在這裏插入圖片描述

取消請求

有些場景下,我們希望能主動取消請求,比如常見的搜索框案例,在用戶輸入過程中,搜索框的內容也在不斷變化,正常情況每次變化都應該向服務器發送一次請求,但在當用用戶輸入過快的時候,我們不希望每次變化請求都發出去,通常一個解決方案是前端用 debounce 的方案,比如延時 200 ms 發送請求,這樣當用戶連續輸入的字符,只要輸入間隔小於 200 ms,前面輸入的字符都不會發請求。但是還有一種極端情況下,後端接口很慢,比如超過1s才能響應,這個時候即使多了 200ms 的debounce,但是在我慢慢輸入(每個輸入間隔超過200ms)的情況下,在前面的請求沒有響應前,也有可能發出多個請求,因爲接口的響應時長是不定的,如果先發出去的請求響應時比後出發的請求要久一些,後請求的響應先回來,現請求的響應後回來,就會出現起請求響應結果覆蓋後面請求的響應結果的情況,那麼就亂了。因此在這個情況下,我們除了做 debounce,還希望後面的請求發出去的時候,如果前面的請求還沒有響應,我們可以把前面的請求取消。

我修改了 axios.ts 文件

// ...
import CancelToken from './cancel/CancelToken';
import Cancel, { isCancel } from './cancel/Cancel';

// ...
axios.CancelToken = CancelToken;
axios.Cancel = Cancel;
axios.isCancel = isCancel;
// ...

增加了 cancel 目錄

添加了 cancel/CancelToken.ts 文件

import { CancelExecutor, CancelTokenSource, Canceler } from "../types"
import Cancel from './Cancel';
interface ResolvePromise{
    (reason?:Cancel):void
}

export default class CancelToken{
    promise:Promise<Cancel>
    reason?:Cancel

    constructor(executor:CancelExecutor){
        let resolvePromise:ResolvePromise


        this.promise = new Promise<Cancel>(resolve => {
            resolvePromise = resolve;
        })

        executor(message=>{
            if(this.reason) {
                return;
            }
            this.reason = new Cancel(message);
            resolvePromise(this.reason)
        })
    }

    throwIfRequested() {
        if(this.reason) {
            throw this.reason;
        } 
    }

    static source():CancelTokenSource {
        let cancel!: Canceler
        const token = new CancelToken((c: Canceler) => {
            cancel = c;
        })
        return {
            cancel,
            token,
        }
    }
}

這裏巧妙的使用了 Promise ,通過後面的 xhr.ts 文件中進行的 .then 來調用函數。

添加了 cancel/Cancel.ts 文件

export default class Cancel{
    message?: string
    constructor (message?:string) {
        this.message = message;
    }
}

export function isCancel(value: any):boolean{
    return value instanceof Cancel
}

其中 Cancel 類是 CancelToken 的類類型

修改core/dispatchRequest.ts 文件

// ...
import { flattenHeaders } from '../helpers/header';
// ...
export default function dispatchRequst (config :AxiosRequestConfig) : AxiosPromise {
    throwIfCancellationRequested(config);	// 放在最前面
	// ...
}
// ...
function throwIfCancellationRequested (config: AxiosRequestConfig) : void {
    if(config.cancleToken) {
        config.cancleToken.throwIfRequested();
    }
}

修改 xhr.ts 文件

// ...
if(cancleToken) {
    cancleToken.promise.then(reason => {
        request.abort()
        reject(reason)
    })
}
request.send(data);
// ...

修改 types/index.ts文件

export interface AxiosRequestConfig {
	// ...
    cancleToken?:CancelToken,
	// ...
}
// ...
export interface CancelToken{
    promise: Promise<Cancel>,
    reason?: Cancel,
    throwIfRequested():void,
}
export interface Canceler{
    (message?:string):void,
}
export interface CancelExecutor{
    (cancel: Canceler):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,
}

就寫這麼多了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章