手写js - 各种类型的 throttle

简单throttle

写一个throttle很简单,每次运行函数时,都判断一下timerId是否存在即可。存在则不执行,否则立即执行。

一个简单例子(这里的this可以取到调用环境的):

js 复制代码
function throttle (fn, delay) {
    let timerId = null;
    return function (...args) {
        if (timerId) {
            return;
        }
        fn.apply(this, args)
        timerId = setTimeout(() => {
            timerId = null;
        }, delay)
    }
}

带尾执行的throttle

错误改装示例

带尾执行如果基于上一个例子改装,就会遇到问题:按一下,也会执行两次

js 复制代码
// 错误示例
function throttle (fn, delay, tail) {
    let timerId = null;
    return function (...args) {
        if (timerId) {
            return;
        }
        fn.apply(this, args)
        timerId = setTimeout(() => {
            if (tail) {
                fn.apply(this, args)
            }
            timerId = null;
        }, delay)
    }
}

正确尾执行

正确尾执行的时机,是:连续两次按下间隔内按下了

如何判断呢?我们可以用一个变量剩余时间来判断。

txt 复制代码
两次按下间隔内 = 剩余时间 > 0
按下完全停止 = 剩余时间 <= 0

所以,按下时剩余时间小于等于0,是完全停止的,会直接触发运行。

按下时剩余时间大于0,说明已经处于按的过程中,这时候我们添加尾执行计时器,最终在下一次结束时运行。

这样,每次第一次按下只会执行一次,两次间隔内按下会触发尾执行。

(1)如果第一次快结束前,又按下,则不会触发。剩余时间 > 0,正处于尾执行计时器运行过程中。

(2)如果第一次结束后,又按下。剩余时间 <= 0,已经完全停止,此时直接运行。

下面是代码实现:

js 复制代码
      function throttle(fn, delay, tail) {
        let timerId = null;
        let lastRunTime = 0;

        return function (...args) {
          const that = this;

          function run() {
            fn.apply(that, args);
            lastRunTime = Date.now();
            if (timerId) {
              clearTimeout(timerId);
              timerId = null;
            }
          }

          // 剩余间隔时间
          let remain = delay - (Date.now() - lastRunTime);

          // 如果剩余时间 < 0,则说明运行停止,立即重新运行,并清除尾调用计时器
          if (remain <= 0) {
            run();
            return;
          }

          // 如果有剩余间隔时间,说明在运行中,则触发尾调用setTimeout
          // 尾调用
          if (tail) {
            if (timerId) {
              return;
            }

            timerId = setTimeout(() => {
              run();
            }, remain);
          }
        };
      }

测试代码(放在html中)

js 复制代码
const dom = document.getElementById("input");

console.time("-->")
dom.oninput = throttle(() => {
    console.timelog("-->")
},500,{ tail:true })
相关推荐
N***73851 小时前
Vue网络编程详解
前端·javascript·vue.js
e***71671 小时前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端
程序猿小蒜1 小时前
基于springboot的的学生干部管理系统开发与设计
java·前端·spring boot·后端·spring
银空飞羽1 小时前
让Trae CN SOLO自主发挥,看看能做出一个什么样的项目
前端·人工智能·trae
Eshine、2 小时前
解决前端项目中,浏览器无法正常加载带.gz名称的文件
前端·vue3·.gz·.gz名称的js文件无法被加载
用户47949283569153 小时前
别再当 AI 的"人肉定位器"了:一个工具让 React 组件秒定位
前端·aigc·ai编程
WYiQIU4 小时前
面了一次字节前端岗,我才知道何为“造火箭”的极致!
前端·javascript·vue.js·react.js·面试
qq_316837754 小时前
uniapp 观察列表每个元素的曝光时间
前端·javascript·uni-app
小夏同学呀4 小时前
在 Vue 2 中实现 “点击下载条码 → 打开新窗口预览 → 自动唤起浏览器打印” 的功能
前端·javascript·vue.js
芳草萋萋鹦鹉洲哦4 小时前
【vue】导航栏变动后刷新router的几种方法
前端·javascript·vue.js