Morten
Morten

Reputation: 145

Deferred jQuery AJAX wrapper

I'm creating a wrapper for the jQuery AJAX method because my AJAX requests depend on asynchronous work before actually proceeding.

What I'm currently doing is this: http://pastie.org/private/bfdvep4kcdclzupsyddmiq

var ajax = function (options) {
    var deferred = $.Deferred();

    doAsyncWork()
    .done(function (attributes) {
        $.ajax(options)
        .done(function () {
            deferred.resolve.apply(this, arguments);
        })
        .fail(function () {
            deferred.reject.apply(this, arguments);
        });
    })
    .fail(function () {
        deferred.reject();
    });

    return deferred.promise();
};

ajax().readyState // undefined

It has an outer deferred that returns a promise. I fire doAsyncWork() and wait for it to complete before actually doing the AJAX request. Now, while this works, the problem is that when other scripts use the wrapper because they can't access the properties of the jQuery XHR object.

Is there a way to solve this elegantly?

Upvotes: 3

Views: 1922

Answers (2)

Alnitak
Alnitak

Reputation: 340045

There's no way to directly pass the jqXHR object because it isn't created by the time the function returns.

But - don't forget that the jqXHR is available in both the done and fail callbacks, as the third and first parameter respectively.

Upvotes: 0

Fabian Schmengler
Fabian Schmengler

Reputation: 24576

The question is: why should they? Do you try to replace $.ajax() transparently with your wrapper? This will get complicated quickly, as you will have to consider all properties and methods of jqXHR.

In theory, something like this is possible, but I would not call it elegant and not recommend it:

var ajax = function (options) {
    var deferred = $.Deferred();
    var promise = deferred.promise();

    doAsyncWork()
    .done(function (attributes) {
        $.ajax(options)
        .done(function () {
            promise.readyState = 4
            deferred.resolve.apply(this, arguments);
        })
        .fail(function () {
            promise.readyState = 4
            deferred.reject.apply(this, arguments);
        });
    })
    .fail(function () {
        deferred.reject();
    });

    promise.readyState = 0;
    return promise;
};

The other readystate settings should go to the appropiate callbacks (there is no onreadystatechange in JQuery).

So, what to do instead?

If other scripts want to use your wrapper, they will have to use the Deferred API. If for some reason they really need to get access to the jqXHR object, you can supply it like this:

var ajax = function (options, xhrReadyCallback) {
    var deferred = $.Deferred();

    doAsyncWork()
    .done(function (attributes) {
        var xhr = $.ajax(options)
        .done(function () {
            deferred.resolve.apply(this, arguments);
        })
        .fail(function () {
            deferred.reject.apply(this, arguments);
        });
        xhrReadyCallback(xhr);
    })
    .fail(function () {
        deferred.reject();
    });

    return deferred.promise();
};

Usage:

ajax(options, function(xhr) {
    // now available:
    xhr.readyState;
});

Upvotes: 2

Related Questions