Javascript http请求 节流封装代码分享
这个节流类早在一年之前就在用了,现在打开CSDN,发现空空如也,想搞篇文章上去,就选了这个挺好用的节流工具。
交代一下背景:
使用的VUE做工作流页面,每个工作流都有多个节点,有些时候会出现多个节点调用同一个请求获取字典数据,比如城市列表,但是每个节点又是相互独立的,流程走到哪一步,显示的节点就不同,流程走完了,这些节点都会显示,这个时候,其中几个节点(其实是单个vue组件写的)都会请求同一个字典接口,这样明显会造成资源浪费,这些字典接口数据几天甚至几个月都不会变,所以没必要重复请求,只需要共用同一个就好了。有人说,你可以把第一个请求的数据缓存起来,给第二个用,但是你怎么保证第一个请求的数据先回来?当然,我也不想用这种低级手动缓存的高耦合方式来处理,所以我才想到了异步任务控制工具,把数据统一交给 http.js 来处理,业务代码不需要操心数据是怎么来的(真实请求返回的?上一次请求缓存的?I Do Not Care),只有它提供给我就好了,所以最后我直接封装到了 http.js 这个工具里。
直接上代码,封装了2个类 TaskController 以及 Task ,名字你们随意改,主要看思路,挺简单的
tools.js 工具类 部分代码:
// 任务控制器 tools.js
class TaskController {
tasks = {};
addTask(task) {
let lastTask = this.tasks[task.key]
// 判断是否同一个任务
if(lastTask) {
// 前一个相同任务未结束
if(lastTask.finished == false) {
console.log('-----------发现同名未结束任务', task.key)
lastTask.addCallback(task.callbacks)
} else {
console.log('-----------前一个相同任务已结束', task.key)
// 前一个相同任务已结束
task.finishTask(lastTask.res)
}
} else {
// 添加新任务
this.tasks[task.key] = task
task.doTask()
}
}
}
// 任务类
class Task {
finished = false // 任务是否完成的标记
/**
callbacks:
任务携带的回调函数列表,包括自己的,当然也可能包含别的任务扔给自己的(当自己的任务没处理完时,
又新增了一个相同任务,当我的任务完成时,会一并处理自己和别人的任务回调函数)
**/
callbacks = []
key = undefined // 任务的 key,唯一识别身份
event = undefined // 任务的事件,必须返回一个 Promise
res = undefined // 任务完成后拿到的结果
constructor({key, event, params, callback}) {
this.key = key
this.event = event
this.params = params
this.addCallback(callback)
}
addCallback(callbacks) {
this.callbacks = this.callbacks.concat(callbacks)
}
doTask() {
this.event(this.params).then(res => {
// 任务完成
this.finished = true
this.finishTask(res)
})
}
finishTask(res) {
this.res = res
this.callbacks.forEach(cb => {
cb(res)
})
}
}
export {
Task,
TaskController,
}
https.js 完整代码:
// http.js
import axios from 'axios'
import { Task, TaskController } from '@/utils/tools.js'
// 设置超时时间 120秒
// axios.defaults.timeout = 120000;
// 请求响应数据缓存
const responseCache = new TaskController()
// 记录请求次数,防止多个连续请求时loading闪烁
let loadingCounts = 0
let $loading = null;
let $router = null;
let $message = null;
let $store = null;
axios.defaults.headers.common['Authorization'] = 'AUTH_TOKEN';
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.headers.post['Content-Type'] = 'application/json';
axios.interceptors.request.use((config) => {
// config.headers.test = 'I am only a header!';
return config;
}, null, { synchronous: true });
axios.interceptors.response.use((response) => {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
}, (error) => {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
});
const request = (method, url, params, variation = "F", data) => {
let sessionid = localStorage.getItem('sessionid') || ''
return axios({
method: method,
url: url,
params: data ? params : (variation != "F" ? params : undefined),
data: data ? data : (variation == "F" ? params : undefined),
headers:{
__sid: sessionid
}
}).then(res => {
if(res.data.result == 'login') {
localStorage.removeItem("sessionid");
localStorage.removeItem("token");
let url = location.pathname + location.search
sessionStorage.setItem('loginJump',url)
$message.success(res.data.message)
setTimeout(()=>{
$router.replace({
path: '/login'
})
},500)
}
if(res.data.result == 'true' && res.data.message != 'SUCCESS') {
if(url != '/logout'){
$message.success(res.data.message)
}
}
if(res.data.result == 'false' && res.data.message) {
$message.error(res.data.message)
}
return res.data
}).catch((err) => {
console.warn('axios catch err: ', err.message)
$message.error(err.message)
return {
_err: true,
_msg: err.message,
result: false,
message: (err.response.data && err.response.data.message) || err.message || 'system error'
}
});
};
// 带 loading 的请求
const request_2 = (method, url, params, variation = "F", data) => {
if(loadingCounts == 0) {
// console.log('显示 loading')
$loading.show()
}
loadingCounts++
// console.log('loading start counts', loadingCounts)
return request(method, url, params, variation, data).then(res => {
loadingCounts--
// console.log('loading end counts', loadingCounts)
if(loadingCounts <= 0) {
// console.log('隐藏 loading')
$loading.hide()
}
return res
})
}
const upload = (method,url,params,data,responseType) => {
let sessionid = localStorage.getItem('sessionid') || ''
return axios({
method,
url,
params,
data,
responseType,
headers:{
__sid: sessionid
}
}).then(res => {
return res
}).catch((err) => {
console.warn('axios catch err: ', err.message)
$message.error(err.message)
return {
data: {
_err: true,
_msg: err.message,
result: 'false',
message: (err.response.data && err.response.data.message) || err.message || 'system error'
}
}
})
}
// 用于上传和下载文件
const request_3 = (method,url,params,data,responseType) =>{
if(loadingCounts == 0) {
$loading.show()
}
loadingCounts++
return upload(method,url,params,data,responseType).then(res => {
loadingCounts--
if(loadingCounts <= 0) {
$loading.hide()
}
return res
})
}
// 开启数据缓存的请求
const request_4 = ({method, url, params, variation = "F", data, key}) => {
if(!key) return request_2(method, url, params, variation, data);
return new Promise((resolve) => {
let task = new Task({
// 任务唯一 id, 作为 map key
key: `${key}`,
// 任务事件,返回 Promise
event: ({method, url, params, variation, data}) => {
return request_2(method, url, params, variation, data).then(res => {
return res
})
},
// 事件函数 event 的参数
params: { method, url, params, variation, data },
// 事件完成的回调函数
callback: res => {
resolve(res)
}
})
// 把数据缓存到 vuex, 方便随时通过 task.key 拿到所需要的数据
responseCache.addTask(task)
})
}
export default {
request,
request_2,
request_3,
request_4,
install(app) {
$loading = app.config.globalProperties.$loading
$router = app.config.globalProperties.$router
$message = app.config.globalProperties.$message
$store = app.config.globalProperties.$store
app.config.globalProperties.$httpCache = responseCache
}
}
最后在 api.js 中使用:
// api.js 中使用开启数据缓存的请求
export const ASSETS_API = {
// 城市列表
getCityList:(query,data)=>{
// 缓存数据
return request4({
method: 'post',
url: '/xxxxxx/listData',
params: query,
variation: 'F',
data: data,
key: `getCityList${query.pageNo}_${data.queryValues}`,
})
},
}
最后
大概就是这么多,如果大家有更好的建议,请在下边留言补充哈