gpt4 book ai didi

javascript - 基于选择性超时的事件处理 : immediate first, 接下来去抖动

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:10:54 26 4
gpt4 key购买 nike

假设存在随机序列的外部操作(例如滚动事件)。我需要立即处理第一个 Action ,然后取消所有间隔小于某个给定增量的 Action ,然后处理下一个应该为该增量延迟的 Action 。应以相同的方式处理进一步的操作。

这看起来像是 debounce-immediate 和 simple debounce 的组合。我准备了一张图表来展示这个想法。

enter image description here

这里最好的解决方案/方法是什么?不知有没有现成的图案...

更新

我要感谢所有参与者!为了研究,我用 四个 答案中建议的五个不同实现创建了 plunker:https://plnkr.co/N9nAwQ .

const handler = [
processEvent, // normal
debounceNext(processEvent, DELAY), // dhilt
makeRateLimitedEventHandler(DELAY, processEvent), // user650881
debounceWithDelay(processEvent, DELAY, 0), // willem-dhaeseleer
_.debounce(processEvent, DELAY, {leading: true}) // lodash debounce + leading,
debounceish(DELAY, processEvent) //Mikk3lRo
];

一个好消息是 Lodash 有一个 leading-flag debounce 实现来解决这个问题(感谢 Willem D'Haeseleer)。和 here是来自 Mikk3lRo 回答的很酷的演示,他还提供了一些有用的综合。

我调查了来源和结果:形式为 visual指向内存分配的东西...我没有发现任何性能问题,而且 View 似乎还不错。所以最后的比率就是代码本身。所有源代码都已转换为 ES6(如您在 Plunker 中所见),因为我可以对它们进行全面比较。我排除了我的 own try (尽管我喜欢它的外观,但它有点过分)。时间戳版本非常有趣! postDelay 版本很好,虽然它不是一个请求的功能(所以 snippet demo 有两个 lodash 演示的双重延迟)。

我决定不依赖 lodash(换句话说,我当然会使用带前导选项的 lodash debounce),所以我选择了 Mikk3lRo 的 debounceish

PS 我想分享那笔小赏金(不幸的是没有这样的选择)或者甚至从我的声誉中获得更多分数(但不是 200,太多了,会对只有 100 的获胜者不公平)。我什至不能投票两次......没关系。

最佳答案

使用单个计时器的 vanilla JS 中一个非常简单的解决方案:

function debounceish(delta, fn) {
var timer = null;
return function(e) {
if (timer === null) {
//Do now
fn(e);
//Set timer that does nothing (but is not null until it's done!)
timer = setTimeout(function(){
timer = null;
}, delta);
} else {
//Clear existing timer
clearTimeout(timer);
//Set a new one that actually does something
timer = setTimeout(function(){
fn(e);
//Set timer that does nothing again
timer = setTimeout(function(){
timer = null;
}, delta);
}, delta);
}
};
}

function markEvt(e) {
var elm = document.createElement('div');
elm.style.cssText = 'position:absolute;background:tomato;border-radius:3px;width:6px;height:6px;margin:-3px;';
elm.style.top = e.clientY + 'px';
elm.style.left = e.clientX + 'px';
document.body.appendChild(elm);
}

document.addEventListener('click', debounceish(2000, markEvt));
<p>Click somewhere (2000ms delta) !</p>

使用相同类型的可视化比较 6 个提案:

var methods = {
default: function(delay, fn) {
return fn;
},
dhilt_debounceNext: (delay, cb) => {
let timer = null;
let next = null;

const runTimer = (delay, event) => {
timer = setTimeout(() => {
timer = null;
if(next) {
next(event);
next = null;
runTimer(delay);
}
}, delay);
};

return (event) => {
if(!timer) {
cb(event);
}
else {
next = cb;
clearTimeout(timer);
}
runTimer(delay, event);
}
},

Mikk3lRo_debounceish(delta, fn) {
var timer = null;
return function(e) {
if (timer === null) {
//Do now
fn(e);
//Set timer that does nothing (but is not null until it's done!)
timer = setTimeout(function(){
timer = null;
}, delta);
} else {
//Clear existing timer
clearTimeout(timer);
//Set a new one that actually does something
timer = setTimeout(function(){
fn(e);
//Set timer that does nothing again
timer = setTimeout(function(){
timer = null;
}, delta);
}, delta);
}
};
},

user650881_makeRateLimitedEventHandler: function(delta_ms, processEvent) {
var timeoutId = 0; // valid timeoutId's are positive.
var lastEventTimestamp = 0;

var handler = function (evt) {
// Any untriggered handler will be discarded.
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = 0;
}
var curTime = Date.now();
if (curTime < lastEventTimestamp + delta_ms) {
// within delta of last event, postpone handling
timeoutId = setTimeout(function () {
processEvent(evt);
}, delta_ms);
} else {
// long enough since last event, handle now
processEvent(evt);
}

// Set lastEventTimestamp to time of last event after delta test.
lastEventTimestamp = Date.now();
};
return handler;
},

Willem_DHaeseleer_debounceWithDelay: (delay, func) => {
let postDebounceWait;
let timeOutLeading = false;
const debounced = _.debounce((...args) => {
// wrap the handler so we can add an additional timeout to the debounce invocation
if (timeOutLeading) {
/*
for the first invocation we do not want an additional timeout.
We can know this is the leading invocation because,
we set timeOutLeading immediately to false after invoking the debounced function.
This only works because the debounced leading functionality is synchronous it self.
( aka it does not use a trampoline )
*/
func(...args);
} else {
postDebounceWait = setTimeout(() => {
func(...args)
}, delay);
}
}, delay, {leading: true});
return (...args) => {
// wrap the debounced method it self so we can cancel the post delay timer that was invoked by debounced on each invocation.
timeOutLeading = true;
clearTimeout(postDebounceWait);
debounced(...args);
timeOutLeading = false;
}
},

Willem_DHaeseleer_lodashWithLeading: (delta, cb) => {
return _.debounce(cb, delta * 2, {leading: true});
},

Javier_Rey_selfCancelerEventListener: function (delta, fn) {
return function(ev) {
var time = new Date().getTime();
if (ev.target.time && time - ev.target.time < delta) {return;}
ev.target.time = time;
fn(ev);
};
},
};

var method_count = 0;
var colors = ['grey', 'tomato', 'green', 'blue', 'red', 'orange', 'yellow', 'black'];
function markEvt(method) {
var style = 'position:absolute;border-radius:3px;width:6px;height:6px;margin:-3px;';
style += 'background:' + colors[method_count] + ';';
if (method_count > 0) {
style += 'transform:rotate(' + Math.floor(360 * method_count / (Object.keys(methods).length - 1)) + 'deg) translateY(-8px);';
}
var elm = document.createElement('div');
elm.innerHTML = '<span style="width:.8em;height:.8em;border-radius:.4em;display:inline-block;background:' + colors[method_count] + '"></span> ' + method;
document.body.appendChild(elm);

method_count++;
return function(e) {
elm = document.createElement('div');
elm.style.cssText = style;
elm.style.top = e.clientY + 'px';
elm.style.left = e.clientX + 'px';
document.body.appendChild(elm);
};
}

for (var method in methods) {
document.addEventListener('click', methods[method](2000, markEvt(method)));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

请注意,我需要对某些方法进行细微调整以获得通用接口(interface)。改编 Cully 的答案比我愿意付出的努力要多,因为考虑到评论表明它无论如何都没有按照 OP 的要求去做。

很明显,Javier Rey 的方法与其他方法完全不同。 Dhilt,user650881 和我自己的方法似乎是一致的。 Willem D'Haeseleer 的两种方法都有双倍的延迟(和其他细微差别),但似乎也表现一致。据我所知,双重延迟完全是故意的,尽管我对 OP 的理解不是这样。

I would say that Willem D'Haeseleer's lodash method is without a doubt the simplest - if you already use lodash that is. Without external dependencies my method is IMO simplest - but I may be biased on that one ;)

关于javascript - 基于选择性超时的事件处理 : immediate first, 接下来去抖动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46633701/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com