crzyonez777 crzyonez777 - 3 months ago 59
Javascript Question

Callback returns undefined with chrome.storage.sync.get

I'm building a Chrome extension and I wrote this code.

var Options = function(){};

Options.prototype = {

getMode: function(){
return chrome.storage.sync.get("value", function(e){
console.log(e); // it prints 'Object {value: "test"}'.
return e;
});
},

setMode: function(){
chrome.storage.sync.set({"value": "test"}, function(e) {
})
}
}

var options = new Options();
options.setMode();
console.log(options.getMode()); // it prints 'undefined'.


I expected it to print

Object {value: "set up"}


when I call
options.getMode()
, but it prints
undefined
.

Does anyone know how to fix this problem?

Answer

The chrome.storage API is asynchronous - it doesn't return it directly, rather passing it as an argument to the callback function. The function call itself always returns undefined.

This is often used to allow other methods to run without having to wait until something responds or completes - an example of this is setTimeout (only difference is that it returns a timer value, not undefined).

For example, take this:

setTimeout(function () { alert(1); }, 10000);
alert(0);

Because setTimeout is asynchronous, it will not stop all code until the entire function completes, rather returning initially, only calling a function when it is completed later on - this is why 0 comes up before 1.


For this reason, you cannot simply do something like:

// "foo" will always be undefined
var foo = asyncBar(function (e) { return e; }); 

Generally, you should put what you want to do in your callback (the function that is called when the asynchronous function is completed). This is a fairly common way of writing asynchronous code:

function getValue(callback) {
  chrome.storage.sync.get("value", callback);
}

You could place an entire portion of your code inside the callback - there's nothing stopping you from doing so. So instead of doing the following:

console.log(getValue()); // typical synchronous method of doing something

This would probably be a better idea:

// how it would be done in asynchronous code
getValue(function (value) {
  console.log(value);
});