15

If I have a function foo. It receives many calls at a short period of time.

function foo(name) {
  console.log(`Hi ${name}, it is now: `, new Date());
}

Delaying consecutive function invocations (debouncing ) is working fine using lodash .

   const debouncedFoo = _.debounce(foo, 1000 );

However, my target is to NOT execute this whole fleet of invocations even the timeout (1000 ) have elapsed, and consider only the last invocation to be executed .

In other words, if i called debouncedFoo 5 times within 900 ms (which is less than "wait param" 1000ms ), I want foo to be executed only once which is the last (5ᵗʰ) call .

Reading lodash documentation , I understood that debounce is overloaded by 3ʳᵈ argument which is options. I used them and the expected behavior does not happen:

   // first attempt
  const debouncedFoo = _.debounce(foo, 1000, {leading: true} );
  // second attempt
  const debouncedFoo = _.debounce(foo, 1000, {trailing: false} );
Abdennour TOUMI
  • 87,526
  • 38
  • 249
  • 254
  • 1
    Isn't it thottling then that you want? https://lodash.com/docs/#throttle – Kaiido Nov 10 '17 at 04:31
  • 1
    It's been a while but for anyone else (like me) stumbling across this - something that can be overlooked sometimes is accidentally recreating the debounced function on every state change. – AndyO Dec 21 '19 at 12:47
  • 2
    Glorious comment @AndyO . This was true in my case - I was using debounce inside a react component. Re-renders were creating multiple instances of debounce. Fixed by moving function out of the component. You saviour ✌️ – Davey Sep 28 '20 at 13:23

2 Answers2

12

Like @AndyO mentioned, make sure you're not re-creating the debounced function upon every state change.

I had the same problem, so I used useCallback to solve it.

import React, { useCallback } from 'React';
import { debounce } from 'lodash';

const myFunction = () => { // some logic };
const debouncedMyFunction = useCallback(debounce(myFunction, 300), []);

Edit:

If your are already using callback check the dependencies, if dependencies are changing then a new callback will get created on every update. Remove such dependencies and pass them as arguments to your function.

DeltaCap019
  • 6,532
  • 3
  • 48
  • 70
  • That was my case – aminous Apr 01 '22 at 14:41
  • 3
    One can optimize this code snippet by using `useMemo()` instead of `useCallback`: useCallback results in debounce() being invoked on every render, as useCallback will just make sure to return the same ref to the first debounce() call (unless dependencies change). So while useCallback will get you the desired effect (the same function ref to the result of the first debounce() call), It is often better to use useMemo() here, as useMemo will only call debounce() during initial rendering of the component. Example: `const debouncedMyFunction = useMemo( () => debounce(myFunction, 300), []);` – TwoFingerRightClick Apr 27 '22 at 22:43
  • it should be upvoted. – mohit jain Jun 11 '22 at 12:27
4

Not familiar with lodash, but you can implement that behavior easily:

function debounce(cb, duration) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      cb(...args);
    }, duration);
  };
}

function debounce(cb, duration) {
  var timer;
  return function() {
    var args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function() {
      cb.apply(null, args);
    }, duration);
  };
}

var call = debounce(console.log.bind(console), 1000);
setTimeout(function(){ call(200); }, 200);
setTimeout(function(){ call(400); }, 400);
setTimeout(function(){ call(600); }, 600);
setTimeout(function(){ call(800); }, 800);
setTimeout(function(){ call(900); }, 900);
Abdennour TOUMI
  • 87,526
  • 38
  • 249
  • 254
Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
  • 1
    Thank you , I already implemented something like this , however, i want to leverage lodash to mitigate unit-tests in the project, then increase coverage. – Abdennour TOUMI Nov 10 '17 at 03:14