Two things you must do when building your own simple AJAX search

Steve Robinson
Spritle Software
Published in
3 min readJun 30, 2016

TL;DR
1. Perform debouncing to avoid unnecessary requests
2. Remember to cancel old pending requests

Often times our apps need that sleek and handy search box to filter various things. Not a bunch of fields that combine with different logical operators but just one single text field to get the job done. In case your back-end is Rails, you might quickly put together this feature leveraging the Server generated JavaScript Response (SJR) technique (I can foresee some outrage — hold it.) and you might have something similar to below in your front-end.

The server returns a script file that will essentially update the document with the search results (or you might do something else if you do not like SJR).

A seasoned developer might point out several deficiencies in this. But I just want to talk about two things here.

Problem 1: Slower older responses messing with the results.

Lets say someone wants to search for a friend named Rajesh. They would start off by typing r and then followed by a and quickly building up to the full name. As they are doing this, since we’re listening to the keyup event, several AJAX requests would have been fired —

/search?term=r
/search?term=ra
/search?term=raj
/search?term=raje
/search?term=rajes
/search?term=rajesh

Although these requests were fired sequentially in quick succession, the order in which the responses are received will tend to be different in most cases. There are several reasons why this so (network latency, concurrency of the server, etc) including the fact that the first response for term=r, will usually have much more results than the other requests, especially than that for term=rajesh.

And since the responses are evaluated in the order that they are received you might witness weird behavior where you get the expected results which would subsequently be replaced by results of the slowest of the above listed requests.

Fixing this is simple enough. jQuery’s AJAX API provides a way to abort a request. All you need to do is capture the request object and call abort() on it when making a new request. When we do that, our code might end up looking like something like this —

If you know any other way (maybe a more elegant way?) to solve this, leave a response!

Problem 2: Plenty of unnecessary requests

As you might have probably picked up already, we are sending a lot of requests that are not worth anything for the user as they are only looking to search for rajesh and not for raje or ra. Clearly we are wasting our server’s CPU time responding to these requests.

A standard technique to apply to such a problem is Debouncing (borrowed from electronics). Its a simple trick where (in our context) after the user keys in a character, you hold for X milliseconds before firing the request and if there is another character entered during that wait time, we wait for another X ms and once the full wait time has passed we fire the request.

Thus if a person types Rajesh quickly without pausing too much between each character, only one request for term=Rajesh would be fired — which is exactly what we want.

A very simple implementation of debouncing would look this —

As you can see we simply set a timeout for 500ms and if there is a new event fired, we simply clear the older timeout and set a new one. The searchEvents method is eventually called when there are no new events fired within the 500ms timeout. I’ve put together a simple JSFiddle to demonstrate this. Check it out here.

For my applications though, I’ve made use of a well maintained & well documented and lightweight (0.7 kB) library called jQuery-throttle-debounce. Here’s our code after applying debouncing using this library —

As you can see at line#12, we make use of the jQuery-throttle-debounce library by means of the $.debounce function. Pretty self explanatory.

Over the years I’ve ran into a lot of bugs in different apps and use-cases because I did not do the above things and most times its hard to debug them. So it would be best to keep these points in mind and apply them whenever appropriate.

Do let me know your thoughts in the responses. Would love to hear more gotchas from y’all!

--

--