Dan
Dan

Reputation: 1295

moving from async false to async true

Hi so I have a piece of code that run quite a few times

Loader.load('data/map.json')

the idea is I can pass in any json file and the loader will deal with it

so here is the loader in async false format

  var Loader = {
    basePath: '',
    cache: {},
    load: function(name) {
        if(typeof this.cache[name] == 'undefined') {
            var loadUrl = (this.basePath == '') ? name : this.basePath + '/' + name;
            var parameters = {

                url: loadUrl,
                dataType: 'json',
                async: false,
                context: this,
                success: function(data) {
                    this.cache[name] = data;
                }
            };

            $.ajax(parameters);
        }
        return this.cache[name];
   }
};
return Loader;

but this is in async false format and I need to get this to async true format to work better with jQuerymobile so with some help on here I managed to get it to a point where its async

var data = AsyncLoader.load('data/map.json').done(function(data, textStatus, jqXHR) {
            console.log("complete");

        }).fail(function(jqXHR, textStatus, errorThrown) {

            console.log("an error has occurred");

        }).always(function(jqXHR, textStatus) {
            console.log("running");
        });

this call the asynloader

function($) {
var AsyncLoader = {
    basePath: '',
    cache: {},
    load: function(name) {
        if(typeof this.cache[name] == 'undefined') {
            var loadUrl = (this.basePath == '') ? name : this.basePath + '/' + name;
            var parameters = {
                beforeSend: function() { $.mobile.showPageLoadingMsg(); },
                complete: function() {$.mobile.hidePageLoadingMsg(); },
                url: loadUrl,
                dataType: 'json',
                async: true,
                context: this,
                success: function(data) {
                    this.cache[name] = data;
                }
            };

            $.ajax(parameters);
        }

        return this.cache[name];
    }
};

return AsyncLoader;

the problem is to make this work I need to return $.ajax(parameters) rather than return this.cache[name]; otherwise I get a javascript error.. the problem is if I change it to get $.ajax(parameters) when I load in maps.json for a second time it doesn't get the data from the this.cache it will load the json file again which is useless

can anyone help

thanks :)

Upvotes: 1

Views: 1038

Answers (3)

Beetroot-Beetroot
Beetroot-Beetroot

Reputation: 18078

Two suggestions :

  • Avoid this to refer to AsyncLoader

  • Test for existence of an obhject property with Object.hasOwnProperty() rather than typeof .... == 'undefined'.

JS:

function($) {
    var AsyncLoader = {
        basePath: '',
        cache: {},
        load: function(name) {
            var dfrd = $.Deferred();
            if(!AsyncLoader.cache.hasOwnProperty(name)) {
                var loadUrl = (AsyncLoader.basePath == '') ? name : AsyncLoader.basePath + '/' + name;
                $.ajax({
                    beforeSend: function() { console.log("d"); },
                    url: loadUrl,
                    dataType: 'json',
                    async: true,
                    success: function(data) {
                        AsyncLoader.cache[name] = data;
                        dfrd.resolve(data);
                    }
                });
            }
            else {
                return dfrd.resolve(AsyncLoader.cache[name]);
            }
            return dfrd.promise();
        }
    };
    return AsyncLoader;
...
}(jQuery);

Notes

Whereas it would be possible for AsyncLoader.load() to return the data directly when it is already cached, this is not the case when the data needs to be obtained from the server; reason being that the server response is asynchronous. What's needed is a mechanism by which any expression that calls AsyncLoader.load(), can receive the data in exactly the same way, regardless of whether AsyncLoader.load() obtains the data from cache or from the server.

So in the code above, AsyncLoader.load() doesn't attempt to return the data, at least not in the conventional sense; instead, it returns a "promise", which is resolved either immediately if the required data is already available from cache, or later when the server responds.

In both cases, the value passed to dfrd.resolve(...) becomes available within the scope where AsyncLoader.load() is called, as an argument of a function specified in a .done() (or .always() or .then()) method, called on the returned promise (typically by method chaining).

For example :

AsyncLoader.load('myName').done(function(data) {
    console.log(data);
    //do wheatever else is necessary with `data` here.
});

Thus, the objective is achieved. By returning a promise (created within AsyncLoader.load()), the required data is made available outside the scope of AsyncLoader.load(), in exactly the same way, regardless of whether it was plucked from cache or freshly received from the server.

Expressions that call AsyncLoader.load() are completely unaware of whether the data was from cache or not, but must always be written in the correct way to receive data via the returned promise.

Upvotes: 0

Dan
Dan

Reputation: 1295

hmm, this isnt quite working..

if I use this

collection

var data = AsyncLoader.load('data/map.json').done(function(data) {

    var models = [];
    $.each(data.data.locations, function() {
        // RESERVED WORDS YO
        if(this['delete'] == 1) {
            return;
        }

        models.push(new MapLocationModel(this));

    });
    $t.reset(models);

});

async loader

function($) {
var AsyncLoader = {
    basePath: '',
    cache: {},
    load: function(name) {
        if(typeof this.cache[name] == 'undefined') {

            console.log("not cached");

            var deferred = jQuery.Deferred();
            var loadUrl = (this.basePath == '') ? name : this.basePath + '/' + name;
            var parameters = {
                beforeSend: function() { console.log("d"); },
                url: loadUrl,
                dataType: 'json',
                async: true,
                context: this,
                success: function(data) {
                    this.cache[name] = data;
                    deferred.resolve(data);
                }
            };

            $.ajax(parameters);
            return deferred;


        } else {

        console.log("cached");

        return jQuery.Deferred().resolve(this.cache[name]);

        }

    }
};

return AsyncLoader;

I will load in the map data multiple times and will never take it from the cache..

If however I change to async false in the above it works fine, but i need async to be true

Thanks

Upvotes: 0

icktoofay
icktoofay

Reputation: 129011

$.ajax returns a Deferred object. You, too, can return your own Deferred object. Your new code might look a little like this:

load: function(name) {
    // Is the object already in the cache?
    if(Object.prototype.hasOwnProperty.call(this.cache, name)) {
        // Yes! Return a Deferred that's already been resolved.
        return jQuery.Deferred().resolve(this.cache[name]);
    }else{
        // No! We have to load it. Let's still use our own Deferred, though:
        var deferred = jQuery.Deferred();

        // There's our Deferred. Now let's load it.
        jQuery.ajax({
            // ...
        }).done(function(data) {
            // We loaded it successfully!
            // Add it to our cache and resolve the Deferred.
            this.cache[name] = data;
            deferred.resolve(data);
        }).fail(function() {
            // Fail! Pass the failure on to our deferred.
            deferred.reject.apply(deferred, arguments);
        });

        // We've started the AJAX request. Now return our Deferred.
        return deferred;
    }
}

Upvotes: 1

Related Questions