Javascript 浅谈节流和防抖

400 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

Javascript 浅谈节流和防抖

前言

实习的时候有个需求是防止用户短时间内多次提交表单内容。想到两种方法,一个是做防抖的处理,一个是在提交后禁用按钮,等请求返回后再重新启用。然后去了解了节流和防抖的原理和实现。这篇文章主要是简单介绍一下节流和防抖。

一、概念

  1. 节流:在单位时间内,只执行一次回调函数,若在单位时间内多次调用,只有一次会生效。
  2. 防抖:事件被触发后延迟调用函数,若再次触发时,之前函数还没有被调用,则取消之前注册的回调函数并重新注册。

相同点:本质上都是以闭包的形式存在。
不同点:节流是将多次执行变成每隔一段时间执行一次。防抖是将多次执行合并为最后一次执行。

二、简单实现

  1. 节流
function throttle(fn, wait = 50) {
    // pre为上一次触发回调的时间
    let pre = 0
    return function (...args) {
        let context = this
        let now = +new Date()
        if (now - pre >= wait) {
            pre = now;
            fn.apply(context, args);
        }
    }
}
  1. 防抖
function debounce(fn, delay = 50) {
    let timer = 0;
    return function (...args) {
        let context = this
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => { fn.apply(context, args) }, delay);
    }
}
  1. 节流和防抖的结合版

debounce 的问题在于它“太有耐心了”。试想,如果用户的操作十分频繁——他每次都不等 debounce 设置的 delay 时间结束就进行下一次操作,于是每次 debounce 都为该用户重新生成定时器,回调函数被延迟了不计其数次。频繁的延迟会导致用户迟迟得不到响应,用户同样会产生“这个页面卡死了”的观感。

为了解决防抖可能存在的用户体验上的问题,可以用 Throttle 来优化 Debounce。
主要的思路是:
当没有达到预定的时间间隔,采用防抖的策略
达到预定的时间间隔,立即执行一次回调函数,不论防抖策略执行与否。

function throttle(fn, delay) {
    let last = 0, timer = null
    return function (...args) {
        let context = this
        let now = +new Date()
        if (now - last < delay) {
            clearTimeout(timer)
            timer = setTimeout(function () {
                last = now
                fn.apply(context, args)
            }, delay)
        } else {
            last = now
            fn.apply(context, args)
        }
    }
}