摘要:将的,和包含全部请求参数的字符串存入管理器。如满足条件,则当前请求不需要发起。如果参数不同,或者是人为操作,则视为两个不同请求。此时取消中的,并将当前请求重新注册。如果不设置此项,则只会保留最后一次,前面的请求会被掉。
先描述两个场景:
快速点击分页码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 为例,方法可以扩展到所有请求工具
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/103802.html
摘要:一返回值调用外部方法获取的值需要对类型做判断,因为我们对方法返回的值是有期望值类型,但是却不能保证这个接口返回的值一直是同一个类型。 19年目标:消灭英语!我新开了一个公众号记录一个程序员学英语的历程 有提升英语诉求的小伙伴可以关注公众号:csenglish 程序员学英语,每天花10分钟交作业,跟我一起学英语吧 javascript作为一门动态类型语言,具有很高的动态灵活性,当定义函数...
摘要:前端面试总结先说背景,本人年月毕业,去年十月校招到今年月一直在做前端开发工作,年前打算换工作,就重新梳理下面试考点总结包含基础,基础,常见算法和数据结构,框架,计算机网络相关知识,可能有的点很细,有的点很大,参考个人情况进行总结,方便对知识 前端面试总结 先说背景,本人2018年7月毕业,去年十月校招到今年10月一直在做前端开发工作,年前打算换工作,就重新梳理下面试考点总结包含: ...
阅读 3088·2021-09-23 11:32
阅读 3025·2021-09-22 15:12
阅读 1797·2019-08-30 14:07
阅读 3605·2019-08-29 16:59
阅读 1734·2019-08-29 11:11
阅读 2396·2019-08-26 13:50
阅读 2495·2019-08-26 13:49
阅读 2688·2019-08-26 11:49