0

I have simple autocomplete input field with Javascript like this:

$('#search').on('keyup', function () {
    var query = $(this).val();
    $.ajax({
        type: "GET",
        url: "/search",
        data: { query: query }
    }).done(function (results) {
        showSearchResults(results);
    });
});

Sometimes first call takes more time then second or third and results are overridden.

How can I make sure that results only from the latest successful call are displayed?

I mean if I got response from call #3 - I no longer care about calls #1 and #2 and don't want them to override results of call #3.

Xweque
  • 605
  • 1
  • 9
  • 29
Yuriy Kvartsyanyy
  • 2,656
  • 2
  • 14
  • 16

2 Answers2

1

Ajax function is in default asynchronous it means that many of functions can run on same time. If You wrote 3 letters it will run 3 times, after 3 keyups. If you want to run function in sequence just add setting async: false.

$.ajax({
    type: "GET",
    url: "/search",
    async: false,
    data: { query: query }
}).done(function (results) {
    showSearchResults(results);
});

But i think You should add some delay, so function will not run immediately after every keyup, just after last one.

Boogeroos
  • 104
  • 5
0

I suggest that you bind an incremental id to each ajax request that you send. When you get a response, just check that it carries last given id.

let lastXHRid=0; // tracker of last sent ajax request

$('#search').on('keyup', function () {
    let reqXHR = $.ajax({ // create a variable out of $.ajax returned value
        type: "GET",
        url: "/search",
        data: { query: $(this).val() }
    });

    lastXHRid++; // increment the XHR counter
    reqXHR.id = lastXHRid; // attach id to the request

    reqXHR.done(function(results, status, respXHR) {
        if ( respXHR.id == lastXHRid ){ // compare id of received and last sent requests
            showSearchResults(results);
        }
    });
});

(Edit: I initially suggested tracking the unix timestamp of last sent request, but as @seth-battis suggested in the comments, a sequence number is far enough. As a bonus, I also debugged my sample snippet!)

Abrab
  • 751
  • 1
  • 8
  • 19
  • 1
    great insight. I used your suggestion just now to great effect. But I think the timestamp is probably overkill -- and, theoretically, could still have collisions. Just use a sequence number instead of the timestamp. If the sequence number matches when the promise returns, we're it... if not, we're old news. – Seth Battis Dec 29 '20 at 21:43