【JS HTTP】Javascript http请求 节流封装代码分享

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

最后

大概就是这么多,如果大家有更好的建议,请在下边留言补充哈

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值