Featured image of post JavaScript Throttling and Debouncing with Lodash ...

JavaScript Throttling and Debouncing with Lodash ...

JavaScript Throttling and Debouncing with Lodash and Other Methods

Modern web applications often suffer from performance bottlenecks caused by frequent event triggers.

Scrolling, resizing, keypresses, and API calls can overwhelm the browser and server if not optimized properly

. Enter throttling and debouncing—two essential techniques for controlling the execution of functions.


1. What Are Throttling and Debouncing?

1.1 Throttling vs Debouncing: Key Differences

TechniquePurposeExample Use Cases
ThrottlingEnsures a function is executed at most once in a set timeScroll events, API polling, resize events
DebouncingEnsures a function executes only after a delaySearch inputs, form validation, autocomplete

🔹 Throttling is useful when you want regular updates but not too frequently.
🔹 Debouncing is ideal when you only need the final result after a user stops an action.


2. Implementing Throttling in JavaScript

2.1 Vanilla JavaScript Throttling

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function throttle(func, delay) {
    let lastCall = 0;
    return function (...args) {
        const now = Date.now();
        if (now - lastCall >= delay) {
            lastCall = now;
            func.apply(this, args);
        }
    };
}

// Example: Throttling a scroll event
window.addEventListener("scroll", throttle(() => {
    console.log("Throttled Scroll Event");
}, 1000)); // Executes at most once per second

2.2 Throttling with Lodash

Lodash provides a built-in _.throttle function:

1
2
3
4
5
const throttledFunction = _.throttle(() => {
    console.log("Lodash Throttled Function");
}, 1000);

window.addEventListener("scroll", throttledFunction);

🔹 Why use Lodash?

  • Reduces complexity
  • Supports leading/trailing execution
  • Works with UI interactions and API throttling

Customizing Lodash Throttle

1
2
3
const throttledFunction = _.throttle(() => {
    console.log("Throttled with leading and trailing execution");
}, 1000, { leading: true, trailing: false });

3. Implementing Debouncing in JavaScript

3.1 Vanilla JavaScript Debouncing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function debounce(func, delay) {
    let timer;
    return function (...args) {
        clearTimeout(timer);
        timer = setTimeout(() => func.apply(this, args), delay);
    };
}

// Example: Debounce an input event
document.getElementById("search").addEventListener("input", debounce(() => {
    console.log("Debounced Input Event");
}, 500)); // Executes only after 500ms of inactivity

3.2 Debouncing with Lodash

1
2
3
4
5
const debouncedFunction = _.debounce(() => {
    console.log("Lodash Debounced Function");
}, 500);

document.getElementById("search").addEventListener("input", debouncedFunction);

🔹 Lodash debouncing is optimized for performance and supports leading/trailing execution.


4. Using Throttling and Debouncing in Frameworks

4.1 Angular Example (Using RxJS)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import { Component } from '@angular/core';
import { debounceTime } from 'rxjs/operators';
import { Subject } from 'rxjs';

@Component({
  selector: 'app-root',
  template: `<input (input)="onSearch($event.target.value)" placeholder="Search">`
})
export class AppComponent {
  searchSubject = new Subject<string>();

  constructor() {
    this.searchSubject.pipe(debounceTime(500)).subscribe(value => {
      console.log("Debounced Search: ", value);
    });
  }

  onSearch(value: string) {
    this.searchSubject.next(value);
  }
}

🔹 Uses RxJS debounceTime() to delay API requests while typing.


4.2 React Example (Using Lodash Debounce)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, { useState, useCallback } from 'react';
import { debounce } from 'lodash';

const SearchComponent = () => {
    const [query, setQuery] = useState("");

    const handleSearch = useCallback(debounce((value) => {
        console.log("Debounced Search:", value);
    }, 500), []);

    return (
        <input 
            type="text" 
            onChange={(e) => { 
                setQuery(e.target.value); 
                handleSearch(e.target.value); 
            }} 
            placeholder="Search..." 
        />
    );
};

export default SearchComponent;

🔹 Uses Lodash debounce inside a React useCallback to optimize API calls.


4.3 Blazor Example (C# Implementation)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@code {
    private string SearchQuery;
    private Timer debounceTimer;

    private void OnSearchChanged(ChangeEventArgs e) {
        if (debounceTimer != null) debounceTimer.Dispose();
        debounceTimer = new Timer(ExecuteSearch, null, 500, Timeout.Infinite);
    }

    private void ExecuteSearch(object state) {
        Console.WriteLine($"Searching for: {SearchQuery}");
    }
}

<input @bind="SearchQuery" @oninput="OnSearchChanged" placeholder="Search..." />

🔹 Implements a manual debounce using Timer in Blazor.


5. Alternatives to Lodash

LibraryFeatures
RxJSDebounce, Throttle, Observable Streams
Underscore.jsSimilar to Lodash but lighter
Throttle-debounce (npm)Lightweight standalone debounce/throttle library

Example: RxJS Throttling in JavaScript

1
2
3
4
5
6
7
8
import { fromEvent } from 'rxjs';
import { throttleTime } from 'rxjs/operators';

const button = document.getElementById('button');

fromEvent(button, 'click')
  .pipe(throttleTime(1000))
  .subscribe(() => console.log('Throttled Click Event'));

6. Final Thoughts: Best Practices

Use Throttling for continuous events (scroll, resize, API polling)
Use Debouncing for delayed interactions (search, form validation)
Lodash simplifies both techniques with _.throttle and _.debounce
Use framework-specific solutions in Angular, React, and Blazor