有很多方法可以做你想做的事。无论如何,要使其动态工作(添加/删除/配置回调),您需要一个状态。这就是为什么我将所有内容都封装在一个类中。
每个回调都将被独立跟踪(以允许根据请求有不同的延迟)。实例化后,您可以添加更多回调和/或事件。显然,这个示例可以扩展/改进,但我认为这是一个好的开始,它应该满足您的所有要求。
如果您对下面的代码有任何疑问,请随时提问,我们很乐意为您解答。
- 注意:我使用了现代语法,但可以轻松重写以提高兼容性。
// -- THROTTLER --
class Throttler {
constructor(args) {
this.queue = [];
this.throttle = typeof args.throttle === "number" ? args.throttle : 500; // GLOBAL THROTTLE
this.threshold = typeof args.threshold === "number" ? args.threshold : 50; // GLOBAL THRESHOLD
// BIND METHODS
this.addCallback = this.addCallback.bind(this);
this.addEvent = this.addEvent.bind(this);
this.handler = this.handler.bind(this);
if (Array.isArray(args.queue)) {
// SETUP INITIAL CALLBACKS
args.queue.forEach(this.addCallback);
}
if (Array.isArray(args.events)) {
// SETUP INITIAL EVENTS
args.events.forEach(this.addEvent);
}
}
addCallback(cb) {
if (typeof cb === "function") {
this.queue.push({
throttle: this.throttle,
threshold: this.threshold,
callback: cb,
timer: null,
ref: Date.now(),
elapsed: 0
});
} else if (typeof cb === "object") {
this.queue.push({
throttle: cb.debounce ? undefined : this.throttle,
threshold: this.threshold,
...cb,
timer: null,
ref: Date.now(),
elapsed: 0
});
}
}
addEvent(eventName) {
// ATTACH HANDLER
window.addEventListener(eventName, this.handler);
}
handler(e) {
this.queue.forEach((elem) => {
const NOW = Date.now();
if (typeof elem.throttle === "number") { // THROTTLE
elem.elapsed += NOW - elem.ref;
elem.ref = NOW;
if (elem.elapsed >= elem.throttle) {
// EXECUTE CALLBACK
if (typeof elem.callback === "function") {
if (typeof elem.selector !== "string" || (typeof e.target.matches === "function" && e.target.matches(elem.selector))) {
if (typeof elem.eventName !== "string" || e.type === elem.eventName) {
elem.callback(e);
}
}
}
// RESET COUNTER
elem.elapsed = 0;
}
// KILL TIMER
elem.timer && clearInterval(elem.timer);
// RE-CREATE TIMER
elem.timer = setTimeout(() => {
// RESET COUNTER
elem.elapsed = 0;
// RESET TIMER
elem.timer = null;
}, elem.threshold);
} else if (typeof elem.debounce === "number") { // DEBOUNCE
// KILL TIMER
elem.timer && clearInterval(elem.timer);
// RE-CREATE TIMER
elem.timer = setTimeout(() => {
// EXECUTE CALLBACK
if (typeof elem.callback === "function") {
if (typeof elem.selector !== "string" || (typeof e.target.matches === "function" && e.target.matches(elem.selector))) {
if (typeof elem.eventName !== "string" || e.type === elem.eventName) {
elem.callback(e);
}
}
}
// RESET TIMER
elem.timer = null;
}, elem.debounce);
}
});
}
}
// --- USAGE ---
// INSTANTIATE THROTTLER
const myThrottler = new Throttler({
throttle: 1500,
events: ['scroll', 'mousemove'],
queue: [callback1, callback2]
});
// ADD ANOTHER EVENT
myThrottler.addEvent('resize');
// ADD CONDITIONAL CALLBACK
myThrottler.addCallback({
callback: callback3,
selector: '*',
eventName: 'mousemove'
});
// ADD CUSTOM DELAY DEBOUNCED CALLBACK
myThrottler.addCallback({
callback: callback4,
debounce: 2000
});
// ADD CUSTOM DELAY THROTTLED CALLBACK
myThrottler.addCallback({
callback: callback5,
throttle: 3000
});
// --- CALLBACKS ---
function callback1() {
console.log("CB 1");
}
function callback2() {
console.log("CB 2");
}
function callback3() {
console.log("CB 3");
}
function callback4() {
console.log("CB 4");
}
function callback5() {
console.log("CB 5");
}