2

I'm trying to use AJAX (through jQuery) to return a bit of JSON from an API, and then store that JSON in localStorage as a string. Whenever the function runs, I want it to check localStorage for the key, and return that value if it exists. If it does not exist, then it should contact the API for the object, save it to localStorage, and then return it.

The problem that I'm having is this: the function NEVER returns the JSON object the first time (when it's not stored in localStorage). It has no problem saving it to localStorage, and it always pulls it from localStorage just fine, but even right after using the returned object in the previous line, the function won't return it. The console just says "undefined".

The code I'm using is below (edited slightly since the API is private):

window.get_company = function() {
    var full = window.location.host;
    var parts = full.split('.');
    var subdomain = parts[0];

    if ( localStorage.getItem("company_" + subdomain) === null ) {
        $.getJSON("https://api.testingapp.com/subdomains?name=" + subdomain).then( function(data) {
            localStorage.setItem("company_" + subdomain, JSON.stringify(data));
            return JSON.stringify(data);
        });
    } else {
        return localStorage.getItem("company_" + subdomain);
    }
}

Thanks so much for your help!

2
  • 2
    returning something inside a callback for an asynchronous function probably doesn't work the way you think it does. Commented Dec 30, 2013 at 22:43
  • Evidently so. I thought that calling .done() on $.getJSON would execute the code inside of it only once the request was complete, but I guess that isn't the case. Can you help me understand then? I'm particularly confused about why the localStorage key gets the value set by data correctly, but the return doesn't. Like, if the value is being set in localStorage, shouldn't it be doing that before that same value is returned? Commented Dec 31, 2013 at 4:41

2 Answers 2

3

Your call to $.getJSON is asynchronous. The return JSON.stringify(data) doesn’t happen until later, after your original get_company function has returned. One way to deal with this would be to use promises or callbacks.

For example, using jQuery’s Deferred object (promises):

window.get_company = function() {
    var deferred = $.Deferred();
    var full = window.location.host;
    var parts = full.split('.');
    var subdomain = parts[0];

    if ( localStorage.getItem("company_" + subdomain) === null ) {
        $.getJSON("https://api.testingapp.com/subdomains?name=" + subdomain).then(function(data) {
            localStorage.setItem("company_" + subdomain, JSON.stringify(data));
            return deferred.resolve(JSON.stringify(data));
        });
    } else {
        deferred.resolve(localStorage.getItem("company_" + subdomain));
    }

    return deferred;
}

// to use:
window.get_company().then(function(result) {
  // do something with the result
})
Sign up to request clarification or add additional context in comments.

3 Comments

Do you think you could explain a little bit more about how to do this with a promise? I know that the "right" way to do this is with a promise, but that's sort of what I thought I had going, although apparently that is not the case.
I added an example showing the use of jQuery’s $.Deferred promises. A full explanation of promises is too much to go into here, but take a look at this: html5rocks.com/en/tutorials/es6/promises
Thanks so much for your help! I was able to take what you wrote and modify it a little to get something what works for me.
1

In the following link you have some solutions/workarounds:

(Check the SECOND answer, not the accepted one)

Return value for function containing jQuery $.post() function

So, although it's technically possible to make the call synchronous and return the value, it's not recommended. Your method should become async and instead of returning a value, it would call a callback when finished, so you'd have:

window.get_company = function(onSuccess) {
    var full = window.location.host;
    var parts = full.split('.');
    var subdomain = parts[0];

    if ( localStorage.getItem("company_" + subdomain) === null ) {
        $.getJSON("https://api.testingapp.com/subdomains?name=" + subdomain).then( function(data) {
            localStorage.setItem("company_" + subdomain, JSON.stringify(data));
            onSuccess(JSON.stringify(data));
        });
    } else {
        onSuccess(localStorage.getItem("company_" + subdomain));
    }
}

Then, instead of calling like this:

company = window.get_company(); //This fails

You would call

//This works
window.get_company(function(returnValue){
   company = returnValue;
});

That is one way, there are others, like returning a promise

Cheers, from La Paz, Bolivia

2 Comments

promises (as opposed to callbacks) would work much better in this situation.
I'd really like to do this as a promise, as Hattan suggested above. Originally, I was calling .done() instead of .then(), but that didn't work any better. From what I understand about the Promise interface in jQuery, calling .done() on $.getJSON should execute that function only once the request has been successfully completed. Where is the flaw in my understanding?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.