javascript 25 lines · 6 steps

Building a debounce function in JavaScript

A closure remembers a pending timer so a function only fires after calls stop arriving.

Explained by highlit
1function debounce(fn, delay) {
2 let timeoutId = null;
3 
4 function debounced(...args) {
5 const context = this;
6 
7 if (timeoutId !== null) {
8 clearTimeout(timeoutId);
9 }
10 
11 timeoutId = setTimeout(() => {
12 timeoutId = null;
13 fn.apply(context, args);
14 }, delay);
15 }
16 
17 debounced.cancel = function () {
18 if (timeoutId !== null) {
19 clearTimeout(timeoutId);
20 timeoutId = null;
21 }
22 };
23 
24 return debounced;
25}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A closure over timeoutId lets each debounced function privately track its own pending timer.
  2. 2Clearing the previous timer on every call ensures fn only runs once activity pauses for delay ms.
  3. 3Capturing this and args at call time, then replaying them via apply, preserves the original calling context.

Related explainers

javascript
const ROLE_PERMISSIONS = {
  admin: ['users:read', 'users:write', 'billing:read', 'billing:write'],
  manager: ['users:read', 'billing:read'],
  member: ['users:read'],

Role-based permissions middleware in Express

authorization middleware rbac
Intermediate 9 steps
javascript
function attachThousandSeparators(input, { locale = 'en-US' } = {}) {
  const formatter = new Intl.NumberFormat(locale);
  const groupSep = formatter.format(11111).replace(/\d/g, '')[0] || ',';
  const decimalSep = formatter.format(1.1).replace(/\d/g, '')[0] || '.';

Live thousand separators without losing the caret

dom intl caret-preservation
Advanced 8 steps
javascript
import { useReducer, useEffect } from "react";
 
const initialState = { status: "idle", data: null, error: null };
 

Building a data-fetching hook in React

custom-hooks usereducer data-fetching
Intermediate 9 steps
javascript
const express = require('express');
const app = express();
 
app.get('/health', (req, res) => res.json({ status: 'ok' }));

Graceful shutdown in an Express server

graceful-shutdown signal-handling connection-tracking
Advanced 9 steps
ruby
class Api::MessagesController < ApiController
  before_action :authenticate_api_key!
 
  rate_limit to: 100,

Layered API rate limiting in Rails

rate-limiting api-authentication throttling
Intermediate 9 steps
javascript
import { useState, useEffect, useCallback } from 'react';
 
function getColumnCount(width) {
  if (width < 640) return 1;

A responsive column hook in React

custom-hooks debouncing responsive-design
Intermediate 7 steps

Share this explainer

Here's the card — post it anywhere.

Building a debounce function in JavaScript — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code