19

Every example of a debounce function that I've seen so far prevents an action from happening multiple times for a specified time span, and then executes the action one time when the specified time span has elapsed, then resets the timer. For example, the $mdUtil.debounce function that is included in Angular Material.

What I'm looking for is a debounce function that executes the action immediately and then prevents subsequent multiple actions from firing until the timer resets. This has the benefit of the user not having to wait until the debounce time has elapsed until their action is taken while still achieving the goal of debouncing the actions.

Has anyone seen one or had luck creating one?

Update After some more thought, the debounce function should fire the action immediately and then, if the debounced function was called again within the debounce time span, it should fire the action a second time before resetting the timer in case the second call changed any values.

adam0101
  • 29,096
  • 21
  • 96
  • 174

1 Answers1

25

edit: adding jsbin implementation

Lodash's debounce can do both. You'll have to specify whether it's leading or trailing.

https://lodash.com/docs#debounce

_.debounce(sendMail, 300, {
  'leading': true,
  'trailing': false
})

you can also write your own debounced function in just few lines jsbin example:

This will click first then debounce subsequent clicks.

function debounce(func, delay) {
  console.log('debounce called with delay', delay);
  var timer = 0;
  return function debouncedFn() {
    if (Date.now() - timer > delay) {
      func();
    }
    timer = Date.now();
  };
}
Henry Zou
  • 1,809
  • 1
  • 14
  • 19
  • The example you posted is more like a throttling function than a debounce function. It would always omit the last call, which is important to me because I'm updating values on a window resize and I need the latest values. The lodash function works perfectly though. Thanks. – adam0101 Jan 04 '16 at 15:51
  • Throttle will invoke the function every x seconds. Whereas debounce will once prevent user subsequent clicks and will only re-enable once the user hasn't clicked on it for x seconds. My code sample works like the latter example. – Henry Zou Jan 04 '16 at 15:55
  • 2
    Regardless of what you call it, I needed it to execute immediately and again if a call was made during the interval. Using Lodash `_.debounce` with 'leading' and 'trailing' both set to true does it perfectly. Thanks for your help. – adam0101 Jan 04 '16 at 17:41
  • I reread your comment and you're right. I get it now. If you were to call the function continually within the debounce delay time, it would never fire the action. It would only fire once the debounce delay time had elapsed without a call being made. Whereas throttling would allow one action per interval. – adam0101 Jan 04 '16 at 18:12
  • thank you for this. by the way you don't have to put strings around key names without spaces { leading: true, trailing: false } unless you want to that is. – King Friday Nov 11 '17 at 18:09
  • @adam0101 I was confused about your comments "I'm updating values on a window resize and I need the latest values. " If you need the latest value, shouldn't you need the last call even it is within the debounce delay time ? – Qiulang May 07 '19 at 04:44
  • @Qiulang, yes, I wanted it to fire immediately AND get the latest value. That's why I said I needed to change the lodash function to have both `leading` and `trailing` set to `true`. – adam0101 May 07 '19 at 13:51
  • 1
    OK So the sample code shown by Henry Zou won't work for you, you need to use lodash. My problem is even worse because my function returns promise, so the debounced version also needs to return promise. See if it interests you :D https://stackoverflow.com/questions/55919714/my-implementation-of-debounce-axios-request-left-the-promise-in-pending-state-fo – Qiulang May 07 '19 at 15:05
  • Adding `...args` to `return function debouncedFn(...args)` and `func(...args)` offers a bit more flexibility. – ggorlen May 08 '20 at 06:58