摘要:將的,和包含全部請求參數(shù)的字符串存入管理器。如滿足條件,則當前請求不需要發(fā)起。如果參數(shù)不同,或者是人為操作,則視為兩個不同請求。此時取消中的,并將當前請求重新注冊。如果不設置此項,則只會保留最后一次,前面的請求會被掉。
先描述兩個場景:
快速點擊分頁碼1.2.3.4.5...。假設網(wǎng)絡不好或接口速度不佳,此時可能有多個pending中請求。而我們無法控制返回順序。假如用戶最后點擊到分頁5,而最后一個返回的接口是第三頁的。那現(xiàn)在雖然頁碼為5,但實際展示的數(shù)據(jù)卻是第三頁的。
以Vue為例,created中調(diào)用接口A,某watch中也調(diào)用接口A。那在頁面初始化時,A可能被調(diào)用了兩次,如果兩次結果一致,那除了浪費,也不會造成其他嚴重問題??山Y果不一致,會概率復現(xiàn)場景1中描述的問題。
解決辦法其實很多,比如:
調(diào)用時加鎖,判斷該接口是否處于pending?
pending狀態(tài)時,禁用操作按鈕;
但這些方法不可避免的會引入多余狀態(tài)。如果同頁面出現(xiàn)N個接口,情況會更糟糕。如何維護那么多狀態(tài)呢?
其實我們可以在攔截器中解決這些問題,直接貼代碼。邏輯看注釋:
以下以 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] - 負載信息,用來保存你期望管理器幫你存儲的內(nèi)容 */ 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,和包含全部請求參數(shù)的字符串存入管理器。(因為source中包含axios的cancel方法) * 3. 請求發(fā)起前,查看管理器中是否已存在一個請求?如果不存在,那注冊一個進去。 * 4. 如果已經(jīng)存在,則對比參數(shù),及判斷是否為機器。如滿足條件,則當前請求不需要發(fā)起。拋出錯誤 currentRequestCancelled。 * 5. 如果參數(shù)不同,或者是人為操作,則視為兩個不同請求。此時取消 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 會跳過所有約束。 * 通常只有一種場景需要這么做: * 頁面初始化時,相同接口同時發(fā)起多個請求,但參數(shù)不一致,且多次返回的結果都會被使用。如果不設置此項,則只會保留最后一次,前面的請求會被 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() // 多次調(diào)用,控制臺中只有第一次請求完成,并打印 `currentRequestCancelled`. (因為這幾次請求完全一樣) // 如果不捕獲錯誤,控制臺將報 cancelled 或 currentRequestCancelled 錯誤。
以上僅以 Axios 為例,方法可以擴展到所有請求工具
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.hztianpu.com/yun/103802.html
摘要:一返回值調(diào)用外部方法獲取的值需要對類型做判斷,因為我們對方法返回的值是有期望值類型,但是卻不能保證這個接口返回的值一直是同一個類型。 19年目標:消滅英語!我新開了一個公眾號記錄一個程序員學英語的歷程 有提升英語訴求的小伙伴可以關注公眾號:csenglish 程序員學英語,每天花10分鐘交作業(yè),跟我一起學英語吧 javascript作為一門動態(tài)類型語言,具有很高的動態(tài)靈活性,當定義函數(shù)...
摘要:前端面試總結先說背景,本人年月畢業(yè),去年十月校招到今年月一直在做前端開發(fā)工作,年前打算換工作,就重新梳理下面試考點總結包含基礎,基礎,常見算法和數(shù)據(jù)結構,框架,計算機網(wǎng)絡相關知識,可能有的點很細,有的點很大,參考個人情況進行總結,方便對知識 前端面試總結 先說背景,本人2018年7月畢業(yè),去年十月校招到今年10月一直在做前端開發(fā)工作,年前打算換工作,就重新梳理下面試考點總結包含: ...
閱讀 3053·2021-09-23 11:32
閱讀 3007·2021-09-22 15:12
閱讀 1772·2019-08-30 14:07
閱讀 3534·2019-08-29 16:59
閱讀 1720·2019-08-29 11:11
閱讀 2377·2019-08-26 13:50
閱讀 2481·2019-08-26 13:49
閱讀 2673·2019-08-26 11:49