Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/402.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 基于选择性超时的事件处理:先立即,后去盎司_Javascript_Algorithm_Debouncing - Fatal编程技术网

Javascript 基于选择性超时的事件处理:先立即,后去盎司

Javascript 基于选择性超时的事件处理:先立即,后去盎司,javascript,algorithm,debouncing,Javascript,Algorithm,Debouncing,假设存在外部动作的随机序列(例如滚动事件)。我需要立即处理第一个操作,然后消除所有间隔小于某个给定增量的操作,然后处理下一个应该为该增量延迟的操作。应以同样的方式处理进一步的行动 这看起来像是直接去盎司和简单去盎司的组合。我准备了一张图表来说明这个想法 这里的最佳解决方案/方法是什么?我想知道是否有现成的图案 更新 我要感谢所有参与者!在我的研究中,我创建了plunker,并在回答中提出了四种五种不同的认识: 一个好消息是,Lodash有一个领先的旗帜去抖动实现,它满足了这个问题(多亏了Wil

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

这看起来像是直接去盎司和简单去盎司的组合。我准备了一张图表来说明这个想法

这里的最佳解决方案/方法是什么?我想知道是否有现成的图案

更新

我要感谢所有参与者!在我的研究中,我创建了plunker,并在回答中提出了四种五种不同的认识:

一个好消息是,Lodash有一个领先的旗帜去抖动实现,它满足了这个问题(多亏了Willem D'Haeseleer)。这是Mikk3lRo回答中的酷演示,他还提供了一些有用的合成

我调查了来源和结果:表单只是指向内存分配的东西。。。我没有发现任何性能问题,而且视图似乎没有问题。所以最后的比率就是代码本身。所有源代码都转换为ES6(如中所示),因为我可以完全比较它们。我排除了我的建议(这有点过分,尽管我喜欢它的外观)。时间戳版本非常有趣!postDelay版本很不错,尽管它不是一个要求的特性(因此对于两个lodash演示,它有两倍的延迟)

我决定不使用lodash依赖项(换句话说,我肯定会使用leading选项),所以我选择了Mikk3lRo的
debounceish


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

我认为这里有一些东西和你描述的一样有效。如果不是的话,这至少是一件值得去做的事情

//设置事件总线
const start=getMilli()
const bus=createBus()
on('event',e=>console.log(`[${getPassage(start)}][${e}]原始总线:saw事件`)
常量wrappedBus=wrapBus(1600,‘事件’,总线)
wrappedBus.on('event',e=>console.log(`[${getPassage(start)}][${e}]包装总线:saw事件`)
wrappedBus.on('skipped',e=>console.log(`[${getPassage(start)}][${e}]被包装的总线跳过)`)
wrappedBus.on('last before interval',e=>console.log(`[${getPassage(start)}][${e}]这是间隔结束之前的最后一个事件`))
wrappedBus.on('interval tick',979;=>console.log(`[${getPassage(start)}]interval tick`)
//每隔一段时间触发总线上的事件
设totalTime=0
常数间隔时间=300
设置间隔(()=>{
总时间+=间隔时间
总线触发器('事件',总时间)
},休息时间)
函数getMilli(){
return(new Date()).getTime()
}
函数getPassage(from){
从返回getMilli()
}
//创建一个简单的事件总线
函数createBus(){
常数cbs={}
返回{
on:(标签,cb)=>{
if(cbs.hasOwnProperty(label))cbs[label].push(cb)
else cbs[标签]=[cb]
},
触发器:(标签,e)=>{
if(cbs.hasOwnProperty(label))cbs[label].forEach(f=>f(e))
},
}
}
//创建一个新的总线,该总线应按照您描述的方式触发
函数wrapBus(waitInterval、eventLabel、bus){
const newBus=createBus()
让deliveredFirst=false
让GoIgnoreDevent=false
设lastIgnoredEvent=未定义
设置间隔(()=>{
//就在这里,让我们知道这个间隔计时器什么时候开始计时
newBus.trigger('interval tick',null)
//在此间隔结束前推送最后一个事件
如果(GoIgnoreDevent){
gotIgnoredEvent=false
deliveredFirst=false
newBus.trigger(事件标签,lastIgnoredEvent)
newBus.trigger('last before interval',lastIgnoredEvent)
}
},等待时间间隔)
总线on(事件标签,函数(e){
如果(!deliveredFirst){
newBus.trigger(事件标签,e)
deliveredFirst=true
gotIgnoredEvent=false
}
否则{
gotIgnoredEvent=true
lastIgnoredEvent=e
//这里只是查看包装的总线何时跳过事件
newBus.trigger('skipped',e)
}
})
返回纽布斯

}
您可以跟踪上次事件时间,并且仅在需要后续检查时创建计时器事件

function makeRateLimitedEventHandler(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;
}

var DELTA_MS = 5000;
var processEvent = function (evt) { console.log('handling event'); };
el.addEventHandler('some-event', makeRateLimitedEventHandler(DELTA_MS, processEvent));
函数makeRateLimitedEventHandler(delta_ms,processEvent){
var timeoutId=0;//有效的timeoutId为正。
var lastEventTimestamp=0;
变量处理程序=函数(evt){
//任何未迁移的处理程序都将被丢弃。
if(超时ID){
clearTimeout(timeoutId);
timeoutId=0;
}
var curTime=Date.now();
if(curTime
视觉中的行为与带前导选项的标准lodash去抖动行为没有什么不同,唯一的区别是只显示一半的增量而不是完整的增量。
因此,您的解决方案可以如此简单

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

如果希望最后一个延迟更长,可以通过包装取消公告的方法和处理程序来解决这个问题。这样,您可以在handl中设置超时
_.debounce(cb, delta * 2, {leading: true});
const _ = require('lodash');
const bb = require('bluebird');

function handler(arg) {
    console.log(arg, new Date().getSeconds());
}

const debounceWithDelay = (func, delay, postDelay) => {
    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)
            }, postDelay);
        }
    }, 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;
    }
};

const debounceDelay = debounceWithDelay(handler, 50, 2000);

(async function () {
    console.log(new Date().getSeconds());
    debounceDelay(1);
    debounceDelay(2);
    debounceDelay(3);
    debounceDelay(4);
    await bb.delay(3000);
    debounceDelay(5);
    await bb.delay(3000);
    debounceDelay(6);
    debounceDelay(7);
    debounceDelay(8);
})();
const debounceNext = (cb, delay) => { 
  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);
  }
};

const processEvent = (event) => console.log(event);
const debouncedHandler = debounceNext(processEvent, 125);
myElement.addEventListener('scroll', debouncedHandler);