Axios请求防抖与节流简易封装

前言

众所周知,axios是一款使用十分广泛的JS请求库,为我们封装好了非常多的东西。

但是不包括前端较为常见的防抖和节流,那么如果需要的话,我们该怎么做呢?

本文提供一个简单的示例与思路给到大家参考!

防抖和节流的简介

防抖: 用户的连续相同的操作如果间隔不大于某个值(防抖的间隔),则只生效一次。

常见的防抖:用户输入框打字,内容在短时间内不停变化,如果需要监听内容的变化,最好加上防抖,提高性能。

节流: 用户在多个时间段内(节流的间隔)进行了多次相同的操作,在每个时间段内只生效一次。

常见的节流:窗口大小resize更新某些内容时,可以使用节流提升性能。

总的来说,防抖和节流就是为了使回调函数调用不那么频繁,以此提升性能。

封装思路

对于防抖和节流,一个很重要的步骤就是要识别重复操作

对于普通的场景来说,重复的操作可能是用同样的参数调用同样的函数

对于axios 来说,重复的操作就是用重复的参数发起重复的请求,所以我们第一点就是需要识别重复请求。

对于请求,最重要的几个参数就是:urlparamsdata,如果这些参数全部重复,那么就可以认为发起了重复的请求。

甚至还可以更为无脑粗暴,将axios 所有请求的options进行比较,如果一致,就当作重复。

调用设计

封装思路已经大致理清楚,接下来就是设计我们的请求调用方式,参考经典lodashdebouncethrottle方法,设计如下形式的调用:

js 复制代码
    request({
        url:'/api',
        method:'get',
        params:{name:'lsm'},
        debounce:{
            wait:200,
            // leading:false,
            // trailing:true,
            // maxWait:9999
        }
    })
    
    request({
        url:'/api',
        method:'post',
        data:{name:'lsm'},
        throttle:{
            wait:200,
            // leading:false,
            // trailing:true
        }
    })

出于简单,本次的示例就不用到leadingtrailing 等参数,就只用到wait

支持异步的debounceAsync

由于lodash 提供的debounce 方法如果回调函数是异步的,并不能获得异步函数的结果,所以我们需要写一个支持异步的debounceASync方法

js 复制代码
function debounceAsync(fn, wait) {
    let timeout = null;
    return function (...args) {
        return new Promise((resolve, reject) => {
            clearTimeout(timeout);
            timeout = setTimeout(() => {
                try {
                    // 执行原函数,并假设它返回一个Promise
                    const result = fn(...args);
                    if (result instanceof Promise) {
                        // 如果是Promise,则解析这个Promise
                        result.then(resolve).catch(reject);
                    } else {
                        // 如果不是Promise,则直接解析结果
                        resolve(result);
                    }
                } catch (error) {
                    // 如果执行原函数时发生错误,则拒绝Promise
                    reject(error);
                }
            }, wait);
        });
    };
}

throttle 方法也是同理,也需要写一个支持异步的throttleAsync方法,此处我就不提供了,哈哈!

request函数

js 复制代码
import axios from 'axios'
import {debounceAsync,throttleAsync} from './util'

// 存放重复请求方法
const debounceMap = new Map()
const throttleMap = new Map()

function request(options) {
    // 正常情况下的请求,调用axios
    let method = () => {
        return new Promise((resolve, reject) => {
            axios(options)
            .then(res => resolve(res))
            .catch(err => reject(err))
        })
    }
    const { debounce, throttle } = options

    // 将请求参数转为请求唯一标识key
    const key = JSON.stringify(options)
    // 如果开启了防抖
    if (debounce?.wait) {
        // 判断是否已经存在相同的请求
        if (debounceMap.has(key)) {
            // 存在相同的请求,直接从map中获取
            console.log('存在相同请求');
            method = debounceMap.get(key)
        } else {
            // 不存在相同的请求,使用debounceAsync构造一个方法
            console.log('不存在相同请求');
            const { wait, leading = false, trailing = true, maxWait = 9999 } = debounce;
            // 构造防抖请求
            method = debounceAsync(method, wait, { leading, trailing, maxWait })
            // 将该请求放入map中
            debounceMap.set(key, method);
        }
        return method()
    }

    // 如果开启了节流,与防抖同理
    if (throttle?.wait) {
        // 判断是否已经存在相同的请求
        if (throttleMap.has(key)) {
            method = throttleMap.get(key)
        } else {
            const { wait, leading = false, trailing = true } = throttle;
            // 构造防抖请求
            method = throttleAsync(method, wait, { leading, trailing })
            // 将该请求放入map中
            throttleMap.set(key, method);
        }
        return method()
    }

    // 正常情况下
    return method()
}

防抖测试

我们启动一个间隔100毫秒执行一次的调度,在一秒后清除该调度,由于debouncewait设置的时间是200毫秒,正确情况下只会有最后一次请求正常发起并且返回。

js 复制代码
const timer = setInterval(() => {
    request({
        url: 'https://ipapi.co/json',
        method: 'get',
        debounce: {
            wait: 200
        }
    }).then(res => {
        console.log('成功', res.data);
    }).catch(err => {
        console.log('失败', err.message);
    })
}, 100);

setTimeout(() => {
    clearInterval(timer)
}, 1000);

执行结果如下:

yaml 复制代码
不存在相同请求
存在相同请求
存在相同请求
存在相同请求
存在相同请求
存在相同请求
存在相同请求
存在相同请求
存在相同请求
成功 {
  ip: '**********',
  network: '*********',
  version: 'IPv4',
  city: 'Xiamen',
  region: 'Fujian',
  region_code: 'FJ',
  country: 'CN',
  country_name: 'China',
  country_code: 'CN',
  country_code_iso3: 'CHN',
  country_capital: 'Beijing',
  country_tld: '.cn',
  continent_code: 'AS',
  in_eu: false,
  postal: null,
  latitude: 24.4793,
  longitude: 118.0673,
  timezone: 'Asia/Shanghai',
  utc_offset: '+0800',
  country_calling_code: '+86',
  currency: 'CNY',
  currency_name: 'Yuan Renminbi',
  languages: 'zh-CN,yue,wuu,dta,ug,za',
  country_area: 9596960,
  country_population: 1411778724,
  asn: 'AS4809',
  org: 'China Telecom Next Generation Carrier Network'
}

优化

我们还可以给request函数补充一些简写方法:

js 复制代码
    ['get', 'post', 'put', 'delete'].forEach((method) => {
        request[method] = function (options) {
            return request({ ...options, method });
        };
    })
相关推荐
金灰1 分钟前
HTML5--裸体回顾
java·开发语言·前端·javascript·html·html5
茶卡盐佑星_4 分钟前
说说你对es6中promise的理解?
前端·ecmascript·es6
Манго нектар32 分钟前
JavaScript for循环语句
开发语言·前端·javascript
蒲公英100139 分钟前
vue3学习:axios输入城市名称查询该城市天气
前端·vue.js·学习
天涯学馆1 小时前
Deno与Secure TypeScript:安全的后端开发
前端·typescript·deno
以对_1 小时前
uview表单校验不生效问题
前端·uni-app
程序猿小D2 小时前
第二百六十七节 JPA教程 - JPA查询AND条件示例
java·开发语言·前端·数据库·windows·python·jpa
奔跑吧邓邓子2 小时前
npm包管理深度探索:从基础到进阶全面教程!
前端·npm·node.js
前端李易安3 小时前
ajax的原理,使用场景以及如何实现
前端·ajax·okhttp
汪子熙3 小时前
Angular 服务器端应用 ng-state tag 的作用介绍
前端·javascript·angular.js