SCENARIO
Consider the following code:-
[$('#employeeSearchField').keyup(function() {
var query = $(this).val();
$.get('/employees/api/search', { q: query }, function(data) {
// do stuff
...
});
}).trigger('keyup');
When user types an employee’s name, “Mike”, in the search field, a web service call is fired per character typed. In this example, the following web service calls are made:-
- GET /employees/api/search?q=M
- GET /employees/api/search?q=Mi
- GET /employees/api/search?q=Mik
- GET /employees/api/search?q=Mike
Let’s assume this web service searches the input string against databases (or flat files, Facebook API, etc) and returns a list of employee JSON objects where their names match the given input string. The code above will take the result and display the employee list on the view.
PROBLEM
Since we can’t control how long each web service call takes to process the request, the order of the returned JSON objects might not match the order of the web service calls. As a result, we may have stale information being presented on the view. Further, we may have the annoying “flicker” problem where the old employee list overrides the new employee list on the view.
SOLUTION
To ensure the order of the returned JSON objects matches the order of the web service calls, we need to keep track each AJAX call’s timestamp. In this example, I’m using Moment.js, a date library, but you can also use the built-in Date object. For now, think of Moment.js as a Swiss Army Knife for date, or a Rambo Knife for date, or a MacGyver Knife for date… okay, maybe not MacGyver Knife since MacGyver can use a tooth pick to solve the date problem.
// keep track latest AJAX call's timestamp
var latestAjaxCallDateTime;
$('#employeeSearchField').keyup(function() {
var query = $(this).val();
// before executing the AJAX call, store the latest timestamp
latestAjaxCallDateTime = moment();
// set the "soon-to-be-executed" AJAX call's timestamp to be the same
// as the latest timestamp
var currentAjaxCallDateTime = latestAjaxCallDateTime;
$.get('/employees/api/search', { q: query }, function(data) {
// if current timestamp is older than the latest timestamp, then
// omit the request
if (currentAjaxCallDateTime.isBefore(latestAjaxCallDateTime)) {
return;
}
// do stuff
...
});
}).trigger('keyup');
Yes, this looks pretty hacky, but it works. The whole idea is we will not process the result if it is old. The key of making this whole thing work is to set latestAjaxCallDateTime as a global variable and set currentAjaxCallDateTime as a global variable WITHIN the anonymous function of the input field’s keyup event.
Leave a Reply