2

I have a method below:

 self.getOrAddCache = function (key, objectFactory) {
        var data = self.getFromCache(key);
        if (!data) {
            data = objectFactory();
            if (data && data != null)
                self.addToCache(key, data);
        }
        return data;
    };

I use like this:

function getCities()
{
    var cities = getOrAddCache(CacheKeys.Cities, function() {
        var cityArray = new Array();
        // get city informations from service 
        $.ajax({
               type: "GET",
               async: true,
               url: "service/cities",
               success: function (response) {
                    $.each(response, function(index, value) {
                        cityArray.push({
                            name: value.name,
                            id: value.id
                        });
                    });
               }
           });

        if (cityArray.length > 0)
            return cityArray;
        else {
            return null;
        }
    });

    return cities;
}

getCities function always return null because getCities not waiting for completion async ajax request.

How can i resolve this problem? (Request must be async)

T J
  • 42,762
  • 13
  • 83
  • 138
oguzh4n
  • 682
  • 1
  • 10
  • 29
  • for which ajax request getCities waiting for? There's one ajax request inside getCities function. – SachinGutte Nov 16 '12 at 11:28
  • possible duplicate of [Variable doesn't get returned JQuery](http://stackoverflow.com/questions/12475269/variable-doesnt-get-returned-jquery) – Denys Séguret Nov 16 '12 at 11:30
  • this question is a _lot_ more complicated than just adding an async callback. The caching model requires a lot of extra work. – Alnitak Nov 16 '12 at 11:53

4 Answers4

3

The best solution for this is to use Deferred objects. Since you require your AJAX call to be asynchronous, you should have your getCities function return a promise to return that data at some point in the future.

Instead of storing the raw data in the cache, you store those promises.

If you request a promise that has already been resolved, that will complete immediately. If there's already a pending request for the cached object, the async AJAX call will be started and all outstanding callbacks waiting for that promise will be started in sequence.

Something like this should work, although this is of course untested, E&OE, etc, etc.

self.getCached = function(key, objectFactory) {
    var def = self.getCache(key);
    if (!def) {
        def = objectFactory.call(self);
        self.addToCache(key, def);
    }
    return def;
}    

function getCities() {
    return getCached(CacheKeys.Cities, function() {
        return $.ajax({
            type: 'GET',
            url: 'service/cities'
        }).pipe(function(response) {
            return $.map(response, function(value) {
                 return { name: value.name, id: value.id };
            });
        });
    });
}

Note the usage of .pipe to post-process the AJAX response into the required format, with the result being another deferred object, where it's actually the latter one that gets stored in your cache.

The usage would now be:

getCities().done(function(cities) {
    // use the cities array
});
Alnitak
  • 334,560
  • 70
  • 407
  • 495
1

With a callback:

function getCities(callbackFunction)
{
    var cities = getOrAddCache(CacheKeys.Cities, function() {
        var cityArray = new Array();
        // get city informations from service 
        $.ajax({
               type: "GET",
               async: true,
               url: "service/cities",
               success: function (response) {
                    $.each(response, function(index, value) {
                        cityArray.push({
                            name: value.name,
                            id: value.id
                        });
                    });
                    callbackFunction(cityArray);
               }
           });
    });
}

getCities(function(cityArray){
  // do stuff
});
PiTheNumber
  • 22,828
  • 17
  • 107
  • 180
1

You can't return the result from a function fetching asynchronously the data.

Change your getCities function to one accepting a callback :

function fetchCities(callback) {
    var cities = getOrAddCache(CacheKeys.Cities, function() {
        var cityArray = new Array();
        // get city informations from service 
        $.ajax({
               type: "GET",
               async: true,
               url: "service/cities",
               success: function (response) {
                    $.each(response, function(index, value) {
                        cityArray.push({
                            name: value.name,
                            id: value.id
                        });
                    });
                    if (callback) callback(cityArray);
               }
           });
    });
}

And use it like this :

fetchCities(function(cities) {
   // use the cities array
});

Note that it's technically possible, using async:true, to make the code wait for the response but don't use it : that's terrible practice and it locks the page until the server answers.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • ITYM `async: false`. And it's better to return the deferred object than to pass a callback to the function. – Alnitak Nov 16 '12 at 11:46
  • also, this code won't actually work at all. It doesn't take into account the interaction with the OP's cache of requested objects. – Alnitak Nov 16 '12 at 12:41
0

You seem to be contradicting yourself.

Something that is asynchronous, by definition, does not pause the script to wait for the end of it's execution. If it does wait, it cannot be asynchronous.

The best wayto fix this is by adding a callback function in your ajax success function that passes the end result to another function, which handles the rest of the execution.

Flater
  • 12,908
  • 4
  • 39
  • 62
  • see the other answers that also incorrectly make this assumption. Just adding a callback does _not_ solve the problem that the OP's cacheing layer also needs to be populated. – Alnitak Nov 16 '12 at 13:19
  • I see no difference between placing it in `done()` versus `success` (or another function called via `success`). What am I missing? – Flater Nov 16 '12 at 15:08
  • You're missing that the OP's cache needs to get filled with the results of the AJAX call. Just adding a callback function doesn't achieve that. – Alnitak Nov 16 '12 at 15:16