Search code examples
javascriptpromiseecmascript-6es6-promise

JavaScript Promises : Deep nested context with bind(this)


Because I'm using a prototype that has functions calling other functions in the same prototype I have to refer to that method using this

The Problem this Created:

But because of that, I have to preserve a context to use this that has me forming very ugly .bind(this) walls.

Here is a simplified example I made for laughs.

Killmyself.prototype.fireLeMissles = function () {

    return new Promise(function(resolve,reject) {
        this.anotherFunction(param).then(function(result) {

        someList.forEach(function(item) {
          this.fireLeMissles().then(function(anotherResult){
            promiseList.push(anotherResult)
          })
        },this);
        Promise.all(promiseList).then(function(promiseItem){
          childPlacesIds.forEach(function(childPlaceId) {
            //Do Other Stuff
          },this);
        });
      resolve(result);
    }.bind(this).catch(function(err){
      console.log("Yea, life sucks sometimes.")
    }));
  }.bind(this));
}

Killmyself.prototype.another = function(){
   //Other stuff
}

You can see because of calls to functions in the same prototype such as this.anotherFunction(... and this.fireLeMissles(... I had to do deep preservation of context,which now (in my much larger version of this) is making this code hard to work with.

Question:

Is this a "man up and get use to the harder aspects of JavaScript" thing - or do you seasoned developers see simple ways that deep binding like this could have been avoided?


Solution

  • If you are using ES6, you can benefit from arrow functions, which preserve the context.

    var counter = function () {
        this.count = 0;
        setInterval( () => { // arrow function
            console.log(this.count++); // context is preserved
        }, 1000)
    }
    var counter = new counter();
    

    So, your code would become something like:

    Killmyself.prototype.fireLeMissles = function() {
        return new Promise((resolve, reject) => {
            this.anotherFunction(param).then(result => {
                someList.forEach(item => {
                    this.fireLeMissles().then(anotherResult => {
                        promiseList.push(anotherResult)
                    });
                });
                Promise.all(promiseList).then(promiseItem => {
                    childPlacesIds.forEach(childPlaceId => {
                        //Do Other Stuff
                    });
                });
                resolve(result);
            }).catch(err => {
                console.log("Yea, life sucks sometimes.")
            });
        });
    }
    

    For ES5, you can either use .bind exactly the way you did or you can assign this to something else in the function with the desired context, then use that variable inside the inner functions.

    Killmyself.prototype.fireLeMissles = function() {
        var self = this; /// use `self` instead of `this` from now on.
        return new Promise(function(resolve, reject) {
            self.anotherFunction(param).then(function(result) {
                someList.forEach(function(item) {
                    self.fireLeMissles().then(function(anotherResult) {
                        promiseList.push(anotherResult)
                    })
                });
                Promise.all(promiseList).then(function(promiseItem) {
                    childPlacesIds.forEach(function(childPlaceId) {
                        //Do Other Stuff
                    });
                });
                resolve(result);
            }).catch(function(err) {
                console.log("Yea, life sucks sometimes.")
            });
        });
    }