kabforks
kabforks

Reputation: 121

How to add spinner after 500ms pending AJAX request?

I have a trivial problem that I'm eager to solve correctly, with jQuery 3. So the idea is this:

  1. Do AJAX request
  2. Await response. If there is no response after 500ms, show spinner
  3. Hide spinner when we've got a response.

I currently I have something working. But the question is: Is this the correct or preferred way of doing it?

// helper function for setTimeout
function wait(ms) {
    var deferred = $.Deferred();
    setTimeout(function() { deferred.resolve() }, ms);
    return deferred.promise();
}

var request = $.ajax({
    dataType: "json",
    url: "/api/articles/?" + $.param(params)
});

wait(500).then(function() {
    if (request.state() === "pending") // works, but the docs says we should do this only for debugging purposes
        $(".spinner").show();
});

request.always(function() {
    $(".spinner").hide();
);

Can we use request.state() this way? Could this have been solved in a more elegant way?

Upvotes: 1

Views: 1062

Answers (1)

Roamer-1888
Roamer-1888

Reputation: 19288

You should really try to do things like this without synchronous inspection of promise state.

Fairly minor modifications to the code are required.

  • return a Deferred from wait() instead of a promise.
  • call wait(500) and keep a reference to the returned Deferred.
  • show the spinner unconditionally on fulfillemnt of the wait(500) Deferred.
  • reject the wait(500) Deferred on settlement of the ajax promise, thereby ensuring that the timeout can't resolve it later.

This will effectively set up a race between the wait(500) deferred and the $.ajax() promise, the former trying to display the spinner and the latter trying to prevent it.

function delay(ms) {
    return jQuery.Deferred(function(dfrd) {
        setTimeout(dfrd.resolve, ms);
    });
}

var showSpinner = delay(500); // a Deferred that will be resolved after 500ms, unless rejected before that.

showSpinner.then(function() {
    $(".spinner").show();
}, function() {
    console.log('The ajax request settled before the timeout');
});

jQuery.ajax({
    dataType: 'json',
    url: '/api/articles/?' + $.param(params)
}).always(function() {
    $(".spinner").hide(); // hide spinner (if the timeout won the race)
    showSpinner.reject(); // prevent the spinner from being shown
});

Upvotes: 2

Related Questions