摘要:將的,和包含全部請求參數的字符串存入管理器。如滿足條件,則當前請求不需要發起。如果參數不同,或者是人為操作,則視為兩個不同請求。此時取消中的,并將當前請求重新注冊。如果不設置此項,則只會保留最后一次,前面的請求會被掉。
先描述兩個場景:
快速點擊分頁碼1.2.3.4.5...。假設網絡不好或接口速度不佳,此時可能有多個pending中請求。而我們無法控制返回順序。假如用戶最后點擊到分頁5,而最后一個返回的接口是第三頁的。那現在雖然頁碼為5,但實際展示的數據卻是第三頁的。
以Vue為例,created中調用接口A,某watch中也調用接口A。那在頁面初始化時,A可能被調用了兩次,如果兩次結果一致,那除了浪費,也不會造成其他嚴重問題。可結果不一致,會概率復現場景1中描述的問題。
解決辦法其實很多,比如:
調用時加鎖,判斷該接口是否處于pending?
pending狀態時,禁用操作按鈕;
但這些方法不可避免的會引入多余狀態。如果同頁面出現N個接口,情況會更糟糕。如何維護那么多狀態呢?
其實我們可以在攔截器中解決這些問題,直接貼代碼。邏輯看注釋:
以下以 axios 為例。
請求管理器多帶帶封裝管理器,是為了攔截器中的代碼邏輯更清晰,也為擴展性。假設你需要在其他地方獲取所有pending中的請求,并將其全部取消。
注意 cancel() 方法中的 this.pendings[name].source.cancel(),要想此方法有效,我們需要在register請求時,將ajax工具中包含取消請求api的對象作為 source 存入管理器。詳見過濾器中代碼。
/** * requestManage.js 請求管理器 */ class RequestManage { constructor () { if (!RequestManage.instance) { // 這個屬性可以用來判斷是人為操作,還是機器。 this.nerveVelocity = 100 // 進行中的請求 this.pendings = {} RequestManage.instance = this } return RequestManage.instance } /** * 向管理器中注冊請求 * @param {String,Number} name - 請求標識 * @param {*} [payload] - 負載信息,用來保存你期望管理器幫你存儲的內容 */ register (name, payload = {}) { payload.time = new Date() * 1 this.pendings[name] = payload } /** * 取消請求 * @param {String,Number} name - 請求標識 * @param {*} [payload] - 包含負載信息時,銷毀后會重新注冊 */ cancel (name, payload) { this.pendings[name].source.cancel() if (payload) { this.register(name, payload) } } /** * 在管理器中移除制定請求 * @param {String,Number} name - 請求標識 */ remove (name) { delete this.pendings[name] } } export default new RequestManage()過濾器
// request.js import axios from "axios import { requestManage } from "utils" const request = axios.create({ timeout: 5000, headers: { "Content-Type": "application/json", "X-Requested-With": "XMLHttpRequest" } }) /**------------------------------------------------ * axiox request 攔截器 * 整體邏輯: * 1. 使用請求地址 url 作為請求標識。 * 2. 將 axios 的 source,和包含全部請求參數的字符串存入管理器。(因為source中包含axios的cancel方法) * 3. 請求發起前,查看管理器中是否已存在一個請求?如果不存在,那注冊一個進去。 * 4. 如果已經存在,則對比參數,及判斷是否為機器。如滿足條件,則當前請求不需要發起。拋出錯誤 currentRequestCancelled。 * 5. 如果參數不同,或者是人為操作,則視為兩個不同請求。此時取消 pending 中的,并將當前請求重新注冊。 * 6. 使用 escape 配置,人為控制一些特殊接口不受約束。 */ request.interceptors.request.use(config => { const { data, params, url, escape } = config const requestTime = new Date() * 1, source = axios.CancelToken.source(), currentBody = `${JSON.stringify(data)}${JSON.stringify(params)}`, pendingRequest = requestManage.pendings[url], pendingBody = pendingRequest && pendingRequest.body, isRobot = pendingRequest && requestTime - pendingRequest.time < requestManage.nerveVelocity if (pendingRequest) { if (currentBody === pendingBody && isRobot) { return Promise.reject(new Error("currentRequestCancelled")) } else if (!escape) { requestManage.cancel(url, { source: source, body: currentBody }) } } else { requestManage.register(url, { source: source, body: currentBody }) } config.cancelToken = source.token return config }, error => { // 請求錯誤時做些事 return Promise.reject(error) }) /** ------------------------------------------------------------ * axios response 攔截器 * 接口正常返回后,在管理器中把對應請求移除。 * 對 request 時拋出的錯誤做處理。 */ request.interceptors.response.use(response => { const { url } = response.config requestManage.remove(url) return response }, error => { if (axios.isCancel(error)) { throw new Error("cancelled") } else if (error.message === "currentRequestCancelled") { throw new Error("currentRequestCancelled") } else { return Promise.reject(error) } }) export default request封裝API
// api.js import request from "@/utils/request" /** * escape: true 會跳過所有約束。 * 通常只有一種場景需要這么做: * 頁面初始化時,相同接口同時發起多個請求,但參數不一致,且多次返回的結果都會被使用。如果不設置此項,則只會保留最后一次,前面的請求會被 cancel 掉。 */ export default function (params) { return request({ url: `api_path`, method: "GET", params: params, // escape: true }) }使用
import api from "api.js" async function getData () { try { const req = await api({ a:1, b:2 }) } catch (error) { console.log(error) } } getData() getData() getData() getData() // 多次調用,控制臺中只有第一次請求完成,并打印 `currentRequestCancelled`. (因為這幾次請求完全一樣) // 如果不捕獲錯誤,控制臺將報 cancelled 或 currentRequestCancelled 錯誤。
以上僅以 Axios 為例,方法可以擴展到所有請求工具
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103802.html
摘要:一返回值調用外部方法獲取的值需要對類型做判斷,因為我們對方法返回的值是有期望值類型,但是卻不能保證這個接口返回的值一直是同一個類型。 19年目標:消滅英語!我新開了一個公眾號記錄一個程序員學英語的歷程 有提升英語訴求的小伙伴可以關注公眾號:csenglish 程序員學英語,每天花10分鐘交作業,跟我一起學英語吧 javascript作為一門動態類型語言,具有很高的動態靈活性,當定義函數...
摘要:回調使用封裝入門回調是啥看這里回調是什么方應杭知乎是一種特殊的函數,這個函數被作為參數傳給另一個函數去調用。 回調、使用Promise封裝ajax()、Promise入門 1 回調是啥 call a functioncall a function back callback 看這里:Callback(回調)是什么?---方應杭知乎 callback 是一種特殊的函數,這個函數被作為參數...
閱讀 2947·2021-09-23 11:32
閱讀 2918·2021-09-22 15:12
閱讀 1708·2019-08-30 14:07
閱讀 3448·2019-08-29 16:59
閱讀 1640·2019-08-29 11:11
閱讀 2307·2019-08-26 13:50
閱讀 2426·2019-08-26 13:49
閱讀 2621·2019-08-26 11:49