Best practise: How to deal with Promises and 'this'


#1

I am using a Promise in a controller to get some data via the adapter from the server. The adapter returns the promise and if it is successful I would like to persist it to the store. This may sound a little strange, but this is for a log-in feature and I don’t want to push the user credentials to the store. So, I am basically ending up with something like:

  var promise = adapter.send('login', credentials);
  promise.then(function(account) {
      this.store.push('account', extractedAccount);
  }, function(reason) {
      // some error handling
  });

The problem with that is, that the reference to the controller ‘this’ is no longer available when the promise is fulfilled.

One of the solutions usually seen is to create a local variable called something like ‘that’:

  var that = this;      
  var promise = adapter.send('login', credentials);
  promise.then(function(account) {
      that.store.push('account', extractedAccount);
  }, function(reason) {
      // some error handling
  });

This works, but I have never really like this approach as it clutters up the code and what has previously naturally been ‘this’ now suddenly is something weird like ‘that’. Is there a cleaner way to keep the context of ‘this’?


#2

This is the standard callback context dilemma. You can use underscore’s bind function, jQuery’s proxy function, or the lexical solution that most people use (var self = this;). I’d also check out Kyle Simpson’s book on “this” and scoping (it’s like 80 pages long, and one of the best JavaScript reads out there).


#3

Personally I use Function.prototype.bind() to create functions bound to this,

promise.then(
    function(res) {
        this.store.push("x", res);
    }.bind(this),
    function(err) {
        this.error(err);
    }.bind(this)
);

… which is far from perfect (and requires a polyfill for older/IE browsers), but I can’t stand variations on the mess that is,

var self  = this;
var _this = this;
var that  = this;

Using bind() has the advantage of not requiring additional frameworks (outside of polyfills) and (in my opinion) somewhat cleaner and consistent code.


#4

As has already been mentioned, you can use Function.prototype.bind or assigning this to another variable to keep it available.

Alternatively you could assign the things you want from this which I sometimes find nicer to read because the variables have more meaning:

var store   = this.store;    
var promise = adapter.send('login', credentials);

promise.then(function(account) {
  store.push('account', extractedAccount);
}, function(reason) {
  // some error handling
});

Another option is coffeescript, but that’s a fairly big hammer for this small nail.


#5

I actually like the idea of just keeping a reference to what I actually need from ‘this’ and name it accordingly. I didn’t know about the bind function, so that’s good to know too. Thanks about the recommendation for the book. Will have a look at it too.