Reduce your API calls while searching using Debouncing

Rex P
3 min readNov 30, 2021

A Simple Use Case

So you are implementing a super fast Search functionality in your web application/website. Most simple way to do this would be to fetch results from your server (which in turn make calls to your database) via API calls as user types the search query.

Now this all is fine and good in development environment but in production when you have got a large user base, you want to minimise these calls to your servers (Be it for your Web Server, API Server or Database server). Why? Because hardware limitations my friend, A machine can only serve so many requests concurrently.

Now if you analyse the search functionality implemented, you can see if the user is searching through records and want to find record with search string “Rajesh”, it is really unneccessary to make API calls for — “R”, “Ra”, “Raj”, “Raje”, “Rajes”.

That means the simple idea here is that we should wait for some time to let the user finish his query, Right? But then other questions arises, for how much time or for how many letters we should wait? How will we know user is done and also, what if user wants records starting with “R”?

Here comes the concept of debouncing —

Debouncing by defination means “To remove the small ripple of current that forms when a mechanical switch is pushed in an electrical circuit and makes a series of short contacts.

Whaaaaaattttttttttt?

In Programming context -

Debouncing is a programming practice used to ensure that time-consuming tasks do not fire so often, that it stalls the performance of the web page. In other words, it limits the rate at which a function gets invoked.

So basically it means to delay the instant gratification(I mean API calls). How we can do it?

Using 2 JS concepts -

SetTimeout and Closures

Let’s try to implement the same in a very simple react component

Suppose here is your initial version in React (for HTML part you can make obvious small changes in syntax) -

function App() {let [data, setData] = useState([]);const myOnChange = async (e) => {  let queryString = e.target.value;  let data = await makeAPICall(queryString);  setData(data);}return <><input type="text" onChange={myOnChange}></input>       </>
}

Now to delay the instant gratification what you can do is -

function debouncer(fn, ms) {
let timeout = null;
return function debounced(...args) {
const onComplete = () => {
fn.apply(this, args);
timeout = null;
}
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(onComplete, ms);
};
}
function App() { let [data, setData] = useState([]); const myOnChange = async (e) => {
let queryString = e.target.value;
let data = await makeAPICall(queryString);
setData(data);
}
const debouncedOnChange = debouncer(myOnChange, 1000);
return <><input type="text" onChange={debouncedOnChange}></input> </>
}

Explaination of Debouncer function -

There are total 4 functions used for debouncer function —

  1. debouncer
  2. debounced
  3. onComplete
  4. fn- Function that is passed as argument to debouncer initially.

So debouncer function return a function debounced which does 2 things -

  1. It creates a function which will call the function passed to debouncer after specified milliseconds using setTimeout.

2. It clear out any setTimeout that was scheduled by calling the debounced function previously.

This is only possible due to a amazing feature of JavaScript called closure.

The functions in javascript carries with them the environment in which they were created, just like we carry our childhood memories with us.

So even though the setTimeout gets created by returned function debounced, it changes the variable timeout when it is called.

so if can use that variable in next function to cancel the setTimeout, Amazing isn’t it?

--

--