使用promises实现的去抖功能

Chr*_*ris 16 javascript promise

我正在尝试实现一个与javascript中的promise一起使用的去抖函数.这样,每个调用者都可以使用Promise消耗"debounced"函数的结果.这是迄今为止我能够提出的最好的:

function debounce(inner, ms = 0) {
  let timer = null;
  let promise = null;
  const events = new EventEmitter();  // do I really need this?

  return function (...args) {
    if (timer == null) {
      promise = new Promise(resolve => {
        events.once('done', resolve);
      });
    } else {
      clearTimeout(timer);
    }

    timer = setTimeout(() => {
      events.emit('done', inner(...args));
      timer = null;
    }, ms);

    return promise;
  };
}
Run Code Online (Sandbox Code Playgroud)

理想情况下,我想实现这个实用程序函数而不引入依赖于EventEmitter(或实现我自己的基本版本的EventEmitter),但我想不出办法.有什么想法吗?

Chr*_*ris 28

我找到了一个更好的方法来实现这个承诺:

function debounce(inner, ms = 0) {
  let timer = null;
  let resolves = [];

  return function (...args) {    
    // Run the function after a certain amount of time
    clearTimeout(timer);
    timer = setTimeout(() => {
      // Get the result of the inner function, then apply it to the resolve function of
      // each promise that has been created since the last time the inner function was run
      let result = inner(...args);
      resolves.forEach(r => r(result));
      resolves = [];
    }, ms);

    return new Promise(r => resolves.push(r));
  };
}
Run Code Online (Sandbox Code Playgroud)

我仍然欢迎建议,但新的实现回答了我关于如何实现这个函数的原始问题,而不依赖于EventEmitter(或类似的东西).


小智 10

这是我的实现,只有间隔中的最后一个调用会得到解决。在克里斯的解决方案中,所有呼叫将在它们之间延迟解决,这很好,但是有时我们只需要解决最后一个呼叫。如果我错了纠正我。

function debounce(f, interval) {
  let timer = null;

  return (...args) => {
    clearTimeout(timer);
    return new Promise((resolve) => {
      timer = setTimeout(
        () => resolve(f(...args)),
        interval,
      );
    });
  };
}
Run Code Online (Sandbox Code Playgroud)

  • 这是有道理的:重复调用此函数的客户端每次都会处理已解析的值,并且大多数情况下您不希望多次处理相同的值,...只是最后一个。 (3认同)

Tha*_*you 7

解决一个承诺,取消其他承诺

我见过的许多实现都使问题过于复杂或存在其他卫生问题。在这篇文章中,我们将编写我们自己的debounce. 此实施将 -

  • 在任何给定时间最多有一个待处理的承诺(每个去抖任务)
  • 通过正确取消未决的承诺来阻止内存泄漏
  • 仅解决最新的承诺
  • 通过实时代码演示展示正确的行为

我们debounce用它的两个参数来编写,即task去抖和延迟的毫秒数ms。我们为其本地状态引入一个本地绑定,t-

function debounce (task, ms) {
  let t = { promise: null, cancel: _ => void 0 }
  return async (...args) => {
    try {
      t.cancel()
      t = deferred()
      await t.promise
      await task(...args)
    }
    catch (_) { /* prevent memory leak */ }
  }
}
Run Code Online (Sandbox Code Playgroud)

我们依赖于一个可重用的deferred函数,它创建了一个在几毫秒内解析的新承诺ms。它引入了两个本地绑定,它promise本身,以及它的能力cancel-

function deferred (ms) {
  let cancel, promise = new Promise((resolve, reject) => {
    cancel = reject
    setTimeout(resolve, ms)
  })
  return { promise, cancel }
}
Run Code Online (Sandbox Code Playgroud)

点击计数器示例

在第一个示例中,我们有一个用于计算用户点击次数的按钮。事件侦听器使用 附加debounce,因此计数器仅在指定的持续时间后递增 -

// debounce, deferred
function debounce (task, ms) { let t = { promise: null, cancel: _ => void 0 }; return async (...args) => { try { t.cancel(); t = deferred(ms); await t.promise; await task(...args); } catch (_) { console.log("cleaning up cancelled promise") } } }
function deferred (ms) { let cancel, promise = new Promise((resolve, reject) => { cancel = reject; setTimeout(resolve, ms) }); return { promise, cancel } }

// dom references
const myform = document.forms.myform
const mycounter = myform.mycounter

// event handler
function clickCounter (event) {
  mycounter.value = Number(mycounter.value) + 1
}

// debounced listener
myform.myclicker.addEventListener("click", debounce(clickCounter, 1000))
Run Code Online (Sandbox Code Playgroud)
<form id="myform">
<input name="myclicker" type="button" value="click" />
<output name="mycounter">0</output>
</form>
Run Code Online (Sandbox Code Playgroud)

实时查询示例,“自动完成”

在第二个示例中,我们有一个带有文本输入的表单。我们的search查询附有debounce-

// debounce, deferred
function debounce (task, ms) { let t = { promise: null, cancel: _ => void 0 }; return async (...args) => { try { t.cancel(); t = deferred(ms); await t.promise; await task(...args); } catch (_) { console.log("cleaning up cancelled promise") } } }
function deferred (ms) { let cancel, promise = new Promise((resolve, reject) => { cancel = reject; setTimeout(resolve, ms) }); return { promise, cancel } }

// dom references
const myform = document.forms.myform
const myresult = myform.myresult

// event handler
function search (event) {
  myresult.value = `Searching for: ${event.target.value}`
}

// debounced listener
myform.myquery.addEventListener("keypress", debounce(search, 1000))
Run Code Online (Sandbox Code Playgroud)
<form id="myform">
<input name="myquery" placeholder="Enter a query..." />
<output name="myresult"></output>
</form>
Run Code Online (Sandbox Code Playgroud)


use*_*525 6

我登陆这里是因为我想获得Promise的返回值,但是在underscore.js中反跳才返回了undefined。我最终使用lodash带有Leading = true的版本。它适用于我的情况,因为我不在乎执行是领先还是落后。

https://lodash.com/docs/4.17.4#debounce

_.debounce(somethingThatReturnsAPromise, 300, {
  leading: true,
  trailing: false
})
Run Code Online (Sandbox Code Playgroud)

  • 这对我有用。使用默认选项(“{leading: false, Trailing: true }”),去抖动函数将在第一次调用时返回“undefined”(以及所有后续调用,除非间隔大于“wait”)。 (3认同)