【问题标题】:How do you debounce a function after the first call?第一次调用后如何去抖动函数?
【发布时间】:2020-02-29 06:56:18
【问题描述】:

我正在尝试制作一个类似于debounce 的函数,除了我希望在第一次调用之后立即调用去抖函数,并且在去抖时间限制内第一次调用之后的所有调用都被去抖。例如,如果函数连接到一个按钮,并且您快速重复单击该按钮 10 次,则该函数在第一次按下时被调用,并在自第十次按下后经过 x 毫秒后被第二次调用。

【问题讨论】:

  • Date.now() 为您提供时间戳 - 将呼叫的时间戳与允许通过的最后一个时间戳进行比较应该很简单 - 如果您遇到困难,请发布一个包含详细信息的新问题。跨度>
  • This 是您要查找的文章。因为当你搜索“debounce vs throttle”时它并不总是排在第一位,所以我用一个小技巧重新找到它。它链接自 _.debounce_.throttle 文档。您似乎在寻找_.debounceleading: true_.throttle 用于不希望比设定的时间间隔更频繁地运行函数。

标签: javascript


【解决方案1】:

纯 JavaScript 处理:

除了第一次调用之外,还要求在“去抖动”期间重复调用会增加一个复杂性,如果可能的话,除了时间戳之外,还可以使用计时器来解决这个问题,以便立即进行调用。

但是,由于对函数的调用可能会延迟,因此并不总是可以从函数实时将值返回给调用者。

以下概念代码

  • 在以预定义方式锁定调用的意义上调用进程“锁定”。
  • 向所有调用返回undefined,但不实现回调以允许调用者判断其调用是否已执行,或检索返回值;
  • 如果在尝试添加调用的锁定期之后进行调用,则用于进行延迟调用的实际参数是最近一次调用尝试提供的参数。

function CallLock( toCall, lockout) {
    let argv;
    let lastCall = 0;
    let timer = 0;
    function recall() {
        timer = 0;
        lastCall = Date.now();
        toCall(...argv);
    }
    return function( ...args)  {
        let now = Date.now();
        if(timer == 0) {
            if( now >= lastCall+lockout) {
                lastCall = now;
                toCall( ...args);
            }
            else {
                argv = args;
                timer = setTimeout(recall, lastCall+lockout - now);
            }
        }
        else {
            argv = args; // use most recent arguments
        }
    }
}

// test CallLock
let start;
function demo( msg) {
   console.log( "demo('%s') called. Time is %sms after start", msg, Date.now() - start);
}

let lockDemo = CallLock( demo, 1000);   // 1 second lockout

start = Date.now();
lockDemo("call 1");
setTimeout( lockDemo, 200, "call 2");
setTimeout( lockDemo, 400, "call 3");
setTimeout( lockDemo, 1800, "call 4");

测试代码使用 1 秒的锁定时间。请注意,计时器延迟是不精确的,并且 Date.now() 舍入到最接近的毫秒。预期结果是

  1. 调用 1 是同步进行的,将显示 0 或 1 毫秒的开始时间。
  2. 调用 2 从未被执行 - 它的参数未被使用。
  3. 呼叫 3 已执行,但延迟到第一次呼叫锁定后不久
  4. 呼叫 4 已执行,但也延迟了,因为呼叫 3 执行后的锁定期仍然有效。

【讨论】:

    【解决方案2】:

    听起来像throttle。查看这篇文章以获取difference between throttling and debouncing。如果油门不是您所需要的,那么您应该从头开始实现您所需要的(并为细节添加更多解释)。


    编辑: 所以,是的,这不是油门;在lodash 的前沿调用它是去抖动的;

    _.debounce(yourCallback, 100, {
      'leading': true
    })
    

    【讨论】:

    • 不是油门。参考原帖中的例子
    • @Iamok 是的,这不是油门,我会相应地更新答案;
    • 我正在寻找不涉及安装依赖项的答案,但您确实有解决方案
    • @Iamok Lodash 支持摇树。所以你可以独立导入debounce implementation,而不是添加整个库。
    【解决方案3】:

    我建议不要去抖动。旧的去抖动技术依赖于不完美的 setTimeout。而是尝试使用 requestAnimationFrame,它内置了对 Dom 视觉状态更改的下一个触发器的支持。

    【讨论】:

    • 你不懂 requestAnimationFrame 老兄。
    • requestAnimationFrame 用于以 60 fps 运行动画帧...?
    • 您应该在准备好更新屏幕上的动画时调用此方法。这将要求在浏览器执行下一次重绘之前调用您的动画函数。
    • 这不是我想要做的吗?
    猜你喜欢
    • 1970-01-01
    • 2016-05-21
    • 1970-01-01
    • 2017-04-27
    • 1970-01-01
    • 2018-08-29
    • 1970-01-01
    • 1970-01-01
    • 2023-01-10
    相关资源
    最近更新 更多