Fergal
Fergal

Reputation: 2474

jQuery deferred with non jQuery ajax

I have an application where I'm pulling data from multiple sources, and only when all the data is pulled in, do I want to continue. I want the data as fast as possible so use parallel requests.

To figure out when all requests are done, I'm doing this:

function getData(cb) {

    var count, data;

    count = 0;
    data = {};

    function getDataDone() {
        count++;
        //only call callback when all async requests are done
        if (count === 3 && cb) {
            cb(data);
        }
    }

    foo.doAsync({
        success: function (d) {
            data.foo = d;
            getDataDone();
        }
    });

    bar.doAsync({
        success: function (d) {
            data.bar = d;
            getDataDone();
        }
    });

    $.getJSON("/api/", function (d) {
        data.user = d;
        getDataDone();
    });

} //end getData


getData(function (data) {

    //application data loaded, do stuff
    data.foo;
    data.bar;
    data.user;

});

foo.doAsync, bar.doAsync and $.getJSON requests happen is parallel, when completed, they call getDataDone(), which increments a counter. If the counter is equal to the expected number of requests, run callback.

How might jQuery.deferred be applied in this case? To code works perfectly OK as it is. Is there any benefit to using deferred over what I have?

Upvotes: 3

Views: 948

Answers (2)

Paul Grime
Paul Grime

Reputation: 15104

Using jQuery.when: http://api.jquery.com/jQuery.when/ you can wait for several promises to finish.

You could wrap your doAsync() methods in promises.

For example:

function doAsync (opts) {
    var msg = this.msg;
    var data = { json: JSON.stringify({ msg: msg }) };
    $.ajax("/echo/json/", {
        type: "post",
        data: data
    }).then(opts.success);
}

var foo = { msg: "foo", doAsync: doAsync };
var bar = { msg: "bar", doAsync: doAsync };

function createPromise (doAsyncAble) {
    var dfd = $.Deferred();
    doAsyncAble.doAsync({
        success: function (d) {
            dfd.resolve(d);
        }
    });
    return dfd.promise();
}

function getData(cb) {
    var fooPromise = createPromise(foo);
    var barPromise = createPromise(bar);
    var apiPromise = $.ajax("/echo/json/", {
        type: "post",
        data: { json: JSON.stringify({ data: "api" }) }
    });
    $.when(fooPromise, barPromise, apiPromise).done(function (fooResponse, barResponse, apiResponse) {
        var data = {};
        data.foo = fooResponse;
        data.bar = barResponse;
        data.user = apiResponse[0];
        cb(data);
    });

} //end getData


getData(function (data) {
    console.log(data);
});

Produces:

enter image description here

You could also return a promise from getData rather than passing in a callback.

Upvotes: 1

zerkms
zerkms

Reputation: 254926

var d1 = $.Deferred();
foo.doAsync({
    success: function (d) {
        data.foo = d;
        d1.resolve();
    }
});

var d2 = $.Deferred();
bar.doAsync({
    success: function (d) {
        data.bar = d;
        d2.resolve();
    }
});

var json_deferred = $.getJSON("/api/", function (d) {
    data.user = d;
    getDataDone();
});

$.when(d1, d2, json_deferred).then(function() {
    alert('all requests finished');
});

Is there any benefit to using deferred over what I have?

Yep, deferreds allow more control on the app flow and usually (this case isn't an exception) provide more clear solutions.

Hint: if you only need the result of asynchronous requests in the callback and nothing else - you may pass the result to the resolve call like d1.resolve(d); and then get it as a parameter of then() callback, instead of using shared object as a temporary storage.

In case of getJSON() you could even omit the callback:

var json_deferred = $.getJSON('/api/');

Upvotes: 4

Related Questions