Edward J. Stembler Edward J. Stembler - 1 month ago 5
Javascript Question

Retrieving result of passed-in callback in JavaScript?

I'm attempting to write a small wrapper around

localStorage
similar to how Rails fetches a cached result. In Rails the pattern is like this:

# Data.find is a block which just returns the data if the cache key was not found
data = Rails.cache.fetch(key, options) { Data.find }


In JavaScript I'm having a difficult time getting the result of the callback which is executed to return the data if the cache is empty.

function fetch(key, callback) {
var result = localStorage.getItem(key);
if (result) {
return JSON.parse(result);
} else {
result = callback();
if (result) {
result = JSON.stringify(result);
localStorage.setItem(key, result);
}
return result;
}
}


I'm trying to use it like this:

var data = fetch('projects-delivered', function() {
var result;
d3.json('/projects/delivered.json', function(data) {
result = formatDates(data);
});
return result;
});


Data is always undefinded though. Do I need to pass in another callback, or synchronize some how? Or, is this not even possible with JavaScript's async nature?

Answer

Your AJAX runs asynchronously, so your callback will always return undefined.

You'll have to call your callback from within the getData function:

function fetch(key, getData, callback) {
    var result = localStorage.getItem(key);

    result ? callback(JSON.parse(result)) : getData(function (result) {
        result && localStorage.setItem(key, JSON.stringify(result));

        callback(result);
    }
}

Use it like this:

fetch('projects-delivered', function (callback) {
    d3.json('/projects/delivered.json', function (data) {
        callback(formatDates(data));
    });
}, function (data) {
    // Use the data
});

Alternatively, you can return a promise from your fetch function:

function fetch(key, getData) {
    var result = localStorage.getItem(key);

    if (result) return Promise.resolve(JSON.parse(result));

    return new Promise(function (resolve) {
        getData(function (result) {
            result && localStorage.setItem(key, JSON.stringify(result));

            resolve(result);
        });
    });
}

Then use the returned promise:

var promise = fetch('projects-delivered', function (callback) {
    d3.json('/projects/delivered.json', function (data) {
        callback(formatDates(data));
    });
});

promise.then(function (data) {
    // Use the data
});