-1

Please help me to solve the problem, how can we return a debounced version of input function. check the code below:

const steven = () => {console.log('steven')}
  const debounce = (fn, timeLimit) => {
    // what should come here?
    }
const debouncedSteeven = debounce(steven, 5000) // returns a debounced version of the input function
window.addEventListener('scroll', debouncedSteeven);
Jagdeesh Kumar
  • 1,640
  • 2
  • 13
  • 29

4 Answers4

1

If I understood correctly, you want the function to execute if there is no scroll event for more than X milliseconds. In that case, one solution is to use setTimeout() and clearTimeout(). So everytime the debounced function is called, the timeout is reseted if already exists. The implementation would be something like this:

// Object to hold the timers by function names.
let timers = {};

const steven = () => {console.log('steven')};

const debounce = (fn, timeLimit) =>
{
    return () =>
    {
        if (timers[fn.name])
            clearTimeout(timers[fn.name]);

        timers[fn.name] = setTimeout(fn, timeLimit);
    }
}

const debouncedSteeven = debounce(steven, 5000);
window.addEventListener('scroll', debouncedSteeven);
.as-console {background-color:black !important; color:lime;}
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>

If you instead want to execute the function if there is a gap of X milliseconds between two scroll events, you may try this one:

// Object to hold last time a debounced method is called.
let lastExec = {};

const steven = () => {console.log('steven')};

const debounce = (fn, timeLimit) =>
{
    return () =>
    {
        if (new Date().getTime() - lastExec[fn.name] > timeLimit)
            fn();

        lastExec[fn.name] = new Date().getTime();
    }
}

const debouncedSteeven = debounce(steven, 5000);
window.addEventListener('scroll', debouncedSteeven);
.as-console {background-color:black !important; color:lime;}
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
<p>text</p>
Shidersz
  • 16,846
  • 2
  • 23
  • 48
1

You can use setTimeout inside a higher order function to schedule an execution of the function but then cancel that execution, if the function is called again.

In addition, you can store and pass along the context and the arguments from when the call was made, and call the debounced function with those in order to not lose them. This will allow you to apply debouncing on any function, not only on the ones that don't use this and don't take any parameters:

function debounce(fn, limit = 0) {
  //this will hold the timer identifier from setTimeout
  let timeId;
  
  //return a debounced function
  return function _debounced() {
    //unschedule any previous execution
    clearTimeout(timeId);
    
    //store the arguments
    const args = arguments;
    
    //store the context
    let ctx = this;
    
    //schedule a new execution after the given time limit
    timeId = setTimeout(function() {
      //run the function with the context and arguments passed
      fn.apply(ctx, args);
    }, limit);
  }
}


//using parameters
function add(a, b) {
  console.log("adding", a, b);
  console.log("result", a + b);
}

const debouncedAdd = debounce(add, 1000);

debouncedAdd(1, 2);
debouncedAdd(3, 4);
debouncedAdd(5, 6); //only this gets executed

//using context
const person = {
  name: "Alice",
  sayHello: debounce(function sayHello() {
    console.log("hello, my name is:", this.name)
  }, 1000)
}
  
person.sayHello();
person.name = "Bob";
person.sayHello();
person.name = "Carol";
person.sayHello(); //only this gets executed
VLAZ
  • 26,331
  • 9
  • 49
  • 67
  • Nicely, same idea than mine, but a more generic approach. I just can't figure out how to remove my global object used to store the `ids` returned from setTimeout, and now I can see how to do it (+1). Anyway, I think the OP wanted another thing... – Shidersz Apr 18 '19 at 20:25
-1

Look into setTimeout which basically does what you're asking for.

const debounce = (fn, timeLimit) => () => {
  clearTimeout(this.timeout);
  this.timeout = setTimeout(fn, timeLimit);
};

let myFunc = () => console.log('yo');
let myFuncDebounced = debounce(myFunc);
myFuncDebounced();
myFuncDebounced();
console.log('this will print first cause this aint Tha BOUNCE d!!!');
junvar
  • 11,151
  • 2
  • 30
  • 46
-1

I think that the best solution is with rxjs first install rxjs with npm or yarn. then create an observable from event of scroll and pipe the debounce operator and inside his function the timer operator, all of them imported from rxjs.

fromEvent(window, 'scroll')
  .pipe(debounce(() => timer(1000))
  .subscribe(v => console.log('scrolled'))

this code will log "scrolled" with debounced time of 1000 ms = 1 sec. it's also recomended to unsabscribe at the ends of component's life cycle or code to prevent memory leaks

Lagistos
  • 3,539
  • 1
  • 10
  • 18