日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

axios取消功能的设计与实现

發布時間:2023/12/10 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 axios取消功能的设计与实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

取消功能的設計與實現

#需求分析

有些場景下,我們希望能主動取消請求,比如常見的搜索框案例,在用戶輸入過程中,搜索框的內容也在不斷變化,正常情況每次變化我們都應該向服務端發送一次請求。但是當用戶輸入過快的時候,我們不希望每次變化請求都發出去,通常一個解決方案是前端用 debounce 的方案,比如延時 200ms 發送請求。這樣當用戶連續輸入的字符,只要輸入間隔小于 200ms,前面輸入的字符都不會發請求。

但是還有一種極端情況是后端接口很慢,比如超過 1s 才能響應,這個時候即使做了 200ms 的 debounce,但是在我慢慢輸入(每個輸入間隔超過 200ms)的情況下,在前面的請求沒有響應前,也有可能發出去多個請求。因為接口的響應時長是不定的,如果先發出去的請求響應時長比后發出去的請求要久一些,后請求的響應先回來,先請求的響應后回來,就會出現前面請求響應結果覆蓋后面請求響應結果的情況,那么就亂了。因此在這個場景下,我們除了做 debounce,還希望后面的請求發出去的時候,如果前面的請求還沒有響應,我們可以把前面的請求取消。

從 axios 的取消接口設計層面,我們希望做如下的設計:

const 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.');

我們給?axios?添加一個?CancelToken?的對象,它有一個?source?方法可以返回一個?source?對象,source.token?是在每次請求的時候傳給配置對象中的?cancelToken?屬性,然后在請求發出去之后,我們可以通過?source.cancel?方法取消請求。

我們還支持另一種方式的調用:

const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { cancel = c; }) }); // 取消請求 cancel();

axios.CancelToken?是一個類,我們直接把它實例化的對象傳給請求配置中的?cancelToken?屬性,CancelToken?的構造函數參數支持傳入一個?executor?方法,該方法的參數是一個取消函數?c,我們可以在?executor?方法執行的內部拿到這個取消函數?c,賦值給我們外部定義的?cancel?變量,之后我們可以通過調用這個?cancel?方法來取消請求。

#異步分離的設計方案

通過需求分析,我們知道想要實現取消某次請求,我們需要為該請求配置一個?cancelToken,然后在外部調用一個?cancel?方法。

請求的發送是一個異步過程,最終會執行?xhr.send?方法,xhr?對象提供了?abort?方法,可以把請求取消。因為我們在外部是碰不到?xhr?對象的,所以我們想在執行?cancel?的時候,去執行?xhr.abort?方法。

現在就相當于我們在?xhr?異步請求過程中,插入一段代碼,當我們在外部執行?cancel?函數的時候,會驅動這段代碼的執行,然后執行?xhr.abort?方法取消請求。

我們可以利用 Promise 實現異步分離,也就是在?cancelToken?中保存一個?pending?狀態的 Promise 對象,然后當我們執行?cancel?方法的時候,能夠訪問到這個 Promise 對象,把它從?pending?狀態變成?resolved?狀態,這樣我們就可以在?then?函數中去實現取消請求的邏輯,類似如下的代碼:

if (cancelToken) {cancelToken.promise .then(reason => { request.abort() reject(reason) }) }

#CancelToken 類實現

接下來,我們就來實現這個?CancelToken?類,先來看一下接口定義:

#接口定義

types/index.ts:

export interface AxiosRequestConfig {// ... cancelToken?: CancelToken } export interface CancelToken { promise: Promise<string> reason?: string } export interface Canceler { (message?: string): void } export interface CancelExecutor { (cancel: Canceler): void }

其中?CancelToken?是實例類型的接口定義,Canceler?是取消方法的接口定義,CancelExecutor?是?CancelToken?類構造函數參數的接口定義。

#代碼實現

我們單獨創建?cancel?目錄來管理取消相關的代碼,在?cancel?目錄下創建?CancelToken.ts?文件:

import { CancelExecutor } from '../types' interface ResolvePromise { (reason?: string): void } export default class CancelToken { promise: Promise<string> reason?: string constructor(executor: CancelExecutor) { let resolvePromise: ResolvePromise this.promise = new Promise<string>(resolve => { resolvePromise = resolve }) executor(message => { if (this.reason) { return } this.reason = message resolvePromise(this.reason) }) } }

在?CancelToken?構造函數內部,實例化一個?pending?狀態的 Promise 對象,然后用一個?resolvePromise?變量指向?resolve?函數。接著執行?executor?函數,傳入一個?cancel?函數,在?cancel?函數內部,會調用?resolvePromise?把 Promise 對象從?pending?狀態變為?resolved?狀態。

接著我們在?xhr.ts?中插入一段取消請求的邏輯。

core/xhr.ts:

const { /*....*/ cancelToken } = config if (cancelToken) { cancelToken.promise.then(reason => { request.abort() reject(reason) }) }

這樣就滿足了第二種使用方式,接著我們要實現第一種使用方式,給?CancelToken?擴展靜態接口。

#CancelToken 擴展靜態接口

#接口定義

types/index.ts:

export interface CancelTokenSource {token: CancelToken cancel: Canceler } export interface CancelTokenStatic { new(executor: CancelExecutor): CancelToken source(): CancelTokenSource }

其中?CancelTokenSource?作為?CancelToken?類靜態方法?source?函數的返回值類型,CancelTokenStatic?則作為?CancelToken?類的類類型。

#代碼實現

cancel/CancelToken.ts:

export default class CancelToken { // ... static source(): CancelTokenSource { let cancel!: Canceler const token = new CancelToken(c => { cancel = c }) return { cancel, token } } }

source?的靜態方法很簡單,定義一個?cancel?變量實例化一個?CancelToken?類型的對象,然后在?executor?函數中,把?cancel?指向參數?c?這個取消函數。

這樣就滿足了我們第一種使用方式,但是在第一種使用方式的例子中,我們在捕獲請求的時候,通過?axios.isCancel?來判斷這個錯誤參數 e 是不是一次取消請求導致的錯誤,接下來我們對取消錯誤的原因做一層包裝,并且把給?axios?擴展靜態方法

#Cancel 類實現及 axios 的擴展

#接口定義

export interface Cancel {message?: string } export interface CancelStatic { new(message?: string): Cancel } export interface AxiosStatic extends AxiosInstance { create(config?: AxiosRequestConfig): AxiosInstance CancelToken: CancelTokenStatic Cancel: CancelStatic isCancel: (value: any) => boolean }

其中?Cancel?是實例類型的接口定義,CancelStatic?是類類型的接口定義,并且我們給?axios?擴展了多個靜態方法。

#代碼實現

我在?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?類非常簡單,擁有一個?message?的公共屬性。isCancel?方法也非常簡單,通過?instanceof來判斷傳入的值是不是一個?Cancel?對象。

接著我們對?CancelToken?類中的?reason?類型做修改,把它變成一個?Cancel?類型的實例。

先修改定義部分。

types/index.ts:

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

再修改實現部分:

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

接下來我們給?axios?擴展一些靜態方法,供用戶使用。

axios.ts:

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

#額外邏輯實現

除此之外,我們還需要實現一些額外邏輯,比如當一個請求攜帶的?cancelToken?已經被使用過,那么我們甚至都可以不發送這個請求,只需要拋一個異常即可,并且拋異常的信息就是我們取消的原因,所以我們需要給?CancelToken?擴展一個方法。

先修改定義部分。

types/index.ts:

export interface CancelToken {promise: Promise<Cancel> reason?: Cancel throwIfRequested(): void }

添加一個?throwIfRequested?方法,接下來實現它:

cancel/CancelToken.ts:

export default class CancelToken { // ... throwIfRequested(): void { if (this.reason) { throw this.reason } } }

判斷如果存在?this.reason,說明這個?token?已經被使用過了,直接拋錯。

接下來在發送請求前增加一段邏輯。

core/dispatchRequest.ts:

export default function dispatchRequest(config: AxiosRequestConfig): AxiosPromise { throwIfCancellationRequested(config) processConfig(config) // ... } function throwIfCancellationRequested(config: AxiosRequestConfig): void { if (config.cancelToken) { config.cancelToken.throwIfRequested() } }

發送請求前檢查一下配置的 cancelToken 是否已經使用過了,如果已經被用過則不用法請求,直接拋異常。

#demo 編寫

在?examples?目錄下創建?cancel?目錄,在?cancel?目錄下創建?index.html:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Cancel example</title> </head> <body> <script src="/__build__/cancel.js"></script> </body> </html>

接著創建?app.ts?作為入口文件:

import axios, { Canceler } from '../../src/index' const CancelToken = axios.CancelToken const source = CancelToken.source() axios.get('/cancel/get', { cancelToken: source.token }).catch(function(e) { if (axios.isCancel(e)) { console.log('Request canceled', e.message) } }) setTimeout(() => { source.cancel('Operation canceled by the user.') axios.post('/cancel/post', { a: 1 }, { cancelToken: source.token }).catch(function(e) { if (axios.isCancel(e)) { console.log(e.message) } }) }, 100) let cancel: Canceler axios.get('/cancel/get', { cancelToken: new CancelToken(c => { cancel = c }) }).catch(function(e) { if (axios.isCancel(e)) { console.log('Request canceled') } }) setTimeout(() => { cancel() }, 200)

我們的 demo 展示了 2 種使用方式,也演示了如果一個 token 已經被使用過,則再次攜帶該 token 的請求并不會發送。

至此,我們完成了?ts-axios?的請求取消功能,我們巧妙地利用了 Promise 實現了異步分離。目前官方?axios?庫的一些大的 feature 我們都已經實現了,下面的章節我們就開始補充完善?ts-axios?的其它功能。

轉載于:https://www.cnblogs.com/QianDingwei/p/11403916.html

總結

以上是生活随笔為你收集整理的axios取消功能的设计与实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。