1

I am running into an asynchronous JavaScript problem, having trouble saving a value received in a callback method as an object literal. I am making a Chrome Extension and using the chrome.cookies.getAll method and am able to get the cookies and read them in the callback, but I am not able to save the value to the object. I am still getting the hang of objects and asynchronous JavaScript and could really use some help.

Here's the code

var app = {
    init: function() {
        this.activeCookie = {
            'sid': null
        };
        this.setupSession();
        // shows null
        console.log('session in object : ');
        console.log(this.activeCookie['sid']);
    },

    setupSession: function() {
        var that = this;

        function setCookiesAsync(that, cookie) {
            console.log(cookie);
            for(var i=0; i<cookie.length; ++i) {
                if(cookie[i]['name'] === 'sid') {
                    that.activeCookie['sid'] = cookie[i]['value'];
                    // shows a token
                    console.log('session in callback : ');
                    console.log(that.activeCookie['sid']);
                }
            }
        }

        chrome.cookies.getAll({
            'url': ST.BaseUrl
        }, function (cookie) {
            setCookiesAsync(that, cookie);

        });
    }
};

Since the callback executes after

console.log('session in object : ');
console.log(this.activeCookie['sid']);

this.activeCookie['sid'] is null.

I am wondering how I can save the value to the object so the asynchronous method saves it to the object and the subsequent lines of code execute after setupSession() is complete.

Xan
  • 74,770
  • 16
  • 179
  • 206
poutyboi
  • 149
  • 5
  • 20
  • Sorry for closing this as a dupe with such a well-crafted question (I upvoted as well), but that's _really_ a common topic. – Xan Jul 21 '16 at 15:46
  • yea I know I read a bunch on this, saw some awesome posts on here but still can't save the value to the object. Can I keep it open? Can I change it to keep it open? – poutyboi Jul 21 '16 at 15:47
  • Your `init` function is essentially asynchronous as well, since parts of it are - to make sure code executes truly after _everything_ `init` is supposed to do, it needs to take a callback (or return a Promise) that you can guarantee executes after (i.e. at the end of `setCookiesAsync`) – Xan Jul 21 '16 at 15:47
  • hmm weird, but thank you. so i would need something more like: var app = { init: function(callback) { this.activeCookie = { 'sid': null }; this.setupSession(); } }; but what would my callback function look like then, and would it live inside or outside the object? thank you again – poutyboi Jul 21 '16 at 15:51

1 Answers1

3

While this question is on a very canonical topic, it's worded and scoped well enough that providing a specific solution as well will be helpful.

Parts of init() are executed asynchronously. What this means is that by the time init() finishes executing, not all of its code is yet run. There is no way around that - code immediately after the async part will run before it.

However, if you have some code you want to specifically execute after init (like, for instance, the rest of your code!), you can do this by passing this code as a callback and adding it to an appropriate place:

init: function(callback) {
  this.activeCookie = {
    'sid': null
  };
  this.setupSession(callback);
},

setupSession: function(callback) {
  var that = this;

  function setCookiesAsync(that, cookie) {
    for(var i=0; i<cookie.length; ++i) {
      if(cookie[i]['name'] === 'sid') {
        that.activeCookie['sid'] = cookie[i]['value'];
      }
    }
    // This is the point where all of init() has finished
    if (typeof callback === "function") { callback(); }
  }

  chrome.cookies.getAll({
    'url': ST.BaseUrl
  }, function(cookie) {
    setCookiesAsync(that, cookie);
  });
}

Then you can use the code as follows:

app.init(function() {
  /* code that should execute after init */
});

Alternatively, you can use Promises.

init: function() {
  this.activeCookie = {
    'sid': null
  };
  return this.setupSession(callback); // returns a Promise
},

setupSession: function() {
  return new Promise(function(resolve, reject) {
    var that = this;

    function setCookiesAsync(that, cookie) {
      for(var i=0; i<cookie.length; ++i) {
        if(cookie[i]['name'] === 'sid') {
          that.activeCookie['sid'] = cookie[i]['value'];
        }
      }
      // This is the point where all of init() has finished
      resolve();
    }

    chrome.cookies.getAll({
      'url': ST.BaseUrl
    }, function (cookie) {
      setCookiesAsync(that, cookie);
    });
  });
}

And the usage becomes:

app.init().then(function() {
  /* code that should execute after init */
});
Xan
  • 74,770
  • 16
  • 179
  • 206
  • You may also want to look at my recent [loooooong answer](http://stackoverflow.com/a/38456419/934239) on why asynchronous functions are used in Chrome. – Xan Jul 21 '16 at 16:15
  • you are awesome than you so much Xan that is really helpful. I am still working to understand and use the promise implementation, but the callback one makes perfect sense now. Thank you – poutyboi Jul 21 '16 at 16:26