请求中避免重复请求

废话不多说,直接贴代码

import axios from "axios"

let instance = axios.create({
    baseURL: "/api/"
})

// 发布订阅
class EventEmitter {
    constructor() {
        this.event = {}
    }
    on(type, cbres, cbrej) {
        if (!this.event[type]) {
            this.event[type] = [[cbres, cbrej]]
        } else {
            this.event[type].push([cbres, cbrej])
        }
    }

    emit(type, res, ansType) {
        if (!this.event[type]) return
        else {
            this.event[type].forEach(cbArr => {
                if(ansType === 'resolve') {
                    cbArr[0](res)
                }else{
                    cbArr[1](res)
                }
            });
        }
    }
}


// 根据请求生成对应的key
function generateReqKey(config, hash) {
    const { method, url, params, data } = config;
    return [method, url, JSON.stringify(params), JSON.stringify(data), hash].join("&");
}

// 存储已发送但未响应的请求
const pendingRequest = new Set();
// 发布订阅容器
const ev = new EventEmitter()

// 添加请求拦截器
instance.interceptors.request.use(async (config) => {
    let hash = location.hash
    // 生成请求Key
    let reqKey = generateReqKey(config, hash)
    
    if(pendingRequest.has(reqKey)) {
        // 如果是相同请求,在这里将请求挂起,通过发布订阅来为该请求返回结果
        // 这里需注意,拿到结果后,无论成功与否,都需要return Promise.reject()来中断这次请求,否则请求会正常发送至服务器
        let res = null
        try {
            // 接口成功响应
          res = await new Promise((resolve, reject) => {
                    ev.on(reqKey, resolve, reject)
                })
          return Promise.reject({
                    type: 'limiteResSuccess',
                    val: res
                })
        }catch(limitFunErr) {
            // 接口报错
            return Promise.reject({
                        type: 'limiteResError',
                        val: limitFunErr
                    })
        }
    }else{
        // 将请求的key保存在config
        config.pendKey = reqKey
        pendingRequest.add(reqKey)
    }

    return config;
  }, function (error) {
    return Promise.reject(error);
  });

// 添加响应拦截器
instance.interceptors.response.use(function (response) {
    // 将拿到的结果发布给其他相同的接口
    handleSuccessResponse_limit(response)
    return response;
  }, function (error) {
    return handleErrorResponse_limit(error)
  });

// 接口响应成功
function handleSuccessResponse_limit(response) {
      const reqKey = response.config.pendKey
    if(pendingRequest.has(reqKey)) {
      let x = null
      try {
        x = JSON.parse(JSON.stringify(response))
      }catch(e) {
        x = response
      }
      pendingRequest.delete(reqKey)
      ev.emit(reqKey, x, 'resolve')
      delete ev.reqKey
    }
}

// 接口走失败响应
function handleErrorResponse_limit(error) {
    if(error.type && error.type === 'limiteResSuccess') {
      return Promise.resolve(error.val)
    }else if(error.type && error.type === 'limiteResError') {
      return Promise.reject(error.val);
    }else{
      const reqKey = error.config.pendKey
      if(pendingRequest.has(reqKey)) {
        let x = null
        try {
          x = JSON.parse(JSON.stringify(error))
        }catch(e) {
          x = error
        }
        pendingRequest.delete(reqKey)
        ev.emit(reqKey, x, 'reject')
        delete ev.reqKey
      }
    }
      return Promise.reject(error);
}

export default instance;

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值