节流和防抖

个人学习记录欢迎指正
由于之前在应用节流和防抖时没有注意过主流库的leading,trailing,maxWait等配置。因此在此参考lodash源码重新学习。

1.防抖

上一次调用后延迟X秒后再执行。

1.1 默认版本

参考lodash:默认第一次不执行。

注意:不使用时间戳是因为无法实现延迟

const debounce = function (func, wait) {
  let timeId = null;
  return function (...args) {
    timeId && clearTimeout(timeId);
    timeId = setTimeout(() => func(...args), wait);
  };
};
1.2 支持取消

支持在延迟时间未结束前取消函数执行。

const debounce = function (func, wait) {
  let timeId = null;

  const debounced = function (...args) {
    timeId && clearTimeout(timeId);
    timeId = setTimeout(() => func(...args), wait);
  };

  debounced.cancel = () => clearTimeout(timeId);

  return debounced;
};
1.3 支持绑定this
const debounce = function (func, wait) {
  let timeId = null;

  return function (...args) {
    timeId && clearTimeout(timeId);
    timeId = setTimeout(() => func.call(this, ...args), wait);
  };
};
1.4 支持函数返回值

支持返回promise成功执行时兑现,取消时拒绝

注意:返回值没有必要,只是个人想法。主流库在使用定时器时不考虑返回值

const debounce = function (func, wait) {
  let timeId = null;
  let lastReject = () => {};
  return async function (...args) {
    return new Promise((resolve, reject) => {
      if (timeId) {
        clearTimeout(timeId);
        lastReject();
      }
      lastReject = reject;
      timeId = setTimeout(() => {
        resolve(func(...args));
      }, wait);
    });
  };
};
1.5 支持首次调用后,立即执行

支持在延迟前立即执行

举例:lodash.debounce(() => {}, 1000, { leading: true })

const debounce = function (func, wait, leading) {
  let timeId = null;
  let isInvoke = false;

  return function (...args) {
    timeId && clearTimeout(timeId);
    if (leading && !isInvoke) {
      isInvoke = true;
      func(...args);
    } else {
      timeId = setTimeout(() => func(...args), wait);
    }
  };
};

2.节流

X秒内最多调用一次。

2.1 默认版本

参考lodash:默认第一次执行。

注意:Date.now() - now - wait >= 0不规范,但是Date.now是13位数很大,能够表达节流意思即可。Lodash节流是直接调用防抖函数。

const throttle = function (func, wait) {
  let now = 0;
  return function (...args) {
    if (Date.now() - now - wait >= 0) {
      func(...args);
      now = Date.now();
    }
  };
};
2.2 支持取消

支持在时间戳未到达前取消函数节流。

const throttle = function (func, wait) {
  let now = 0;
  const throttled = function (...args) {
    if (Date.now() - now - wait >= 0) {
      func(...args);
      now = Date.now();
    }
  };
  throttled.cancel = () => (now = 0);
  return throttled;
};
2.3 支持绑定this
const throttle = function (func, wait) {
  let now = 0;
  return function(...args) => {
    if (Date.now() - now - wait >= 0) {
      func.call(this, ...args);
      now = Date.now();
    }
  };
};
2.4 支持函数返回值

支持返回promise成功执行时兑现,取消时拒绝

注意:返回值没有必要,只是个人想法。主流库在使用定时器时不考虑返回值

const throttle = function (func, wait) {
  let now = 0;
  return async function (...args) {
    return new Promise((resolve, reject) => {
      if (Date.now() - now - wait >= 0) {
        resolve(func(...args));
        now = Date.now();
      } else {
        reject("rejected");
      }
    });
  };
};
2.5 支持时限内多次调用首次不调用,在时限后执行一次

默认写法不含此功能,但是lodash等主流库的throttle默认是该功能。

举例:lodash.debounce(() => {}, 1000, { leading: true, trailing: true, maxWait: 1000 })

注意:写法比较复杂(要实现lodash防抖的大部分功能),请参考lodash的debounce源码。下面是简单实现

const throttle = function (func, wait, { leading, trailing }) {
  let timeId = null;
  // 上次调用参数(用于判断在时限内是否多次调用)
  let lastArgs = null;
  // 上次调用时间
  let lastCallTime = 0;

  const leadingEdge = function (now, args) {
    if (now - lastCallTime - wait >= 0) {
      func(...args);
      lastCallTime = leading ? Date.now() : 0;
    }
  };

  const trailingEdge = function (args) {
    if (timeId) return;
    timeId = setTimeout(() => {
      // 判断:多次调用才能触发尾部执行(参考lodash)
      lastArgs && func(...args);
      lastArgs = null;
    }, wait);
  };

  return function (...args) {
    const now = Date.now();

    // 判断与执行:是否不执行头部边界
    !leading && !lastCallTime && (lastCallTime = now);

    // 判断与执行:是否是(1)多次调用(2)没有首次调用
    lastCallTime && (lastArgs = args);

    // 执行:头部边界
    leading && leadingEdge(now, args);

    // 执行:尾部边界
    trailing && trailingEdge(args);
  };
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vanghua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值