个人学习记录欢迎指正
由于之前在应用节流和防抖时没有注意过主流库的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);};
};