最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

JavaScript Promises : Deep nested context with bind(this) - Stack Overflow

programmeradmin1浏览0评论

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?

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?

Share Improve this question asked Jan 12, 2016 at 4:11 Nick PinedaNick Pineda 6,46211 gold badges50 silver badges69 bronze badges 6
  • 1 I would use .bnd(this) if nesting is one level up else would go with the var _this=this and would have referred _this inside nested functions.. – Rayon Commented Jan 12, 2016 at 4:17
  • 4 Since you tagged this with ES6, are you familiar with arrow functions? – loganfsmyth Commented Jan 12, 2016 at 4:22
  • @loganfsmyth I've tried to stick with es5 since I've never thought about using babel with server side coding, but I am familiar that one of the perks of arrow functions, I believe, is that the context is implied. – Nick Pineda Commented Jan 12, 2016 at 4:55
  • Good to clarify, since you tagged ES6, I assumed you were asking for an ES6 solution. Node 4 and 5 support arrow functions and promises natively, so many people use them. – loganfsmyth Commented Jan 12, 2016 at 5:05
  • @loganfsmyth Sorry I forgot to tag es5 as well. I just assumed either I was pigeonholing myself into a poor design, or that a slicker es6 solution(which I haven't dived into yet) existed for these kind of purposes. – Nick Pineda Commented Jan 12, 2016 at 5:20
 |  Show 1 more comment

3 Answers 3

Reset to default 16

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.")
        });
    });
}

for starters I do not understand you you need a new Promise.. here, like @loganfsmyth said, I would simply use arrow functions and reduce the complexity:

Killmyself.prototype.fireLeMissles = function (param) {

  return this.anotherFunction(param)
  .then(someList => {
    var promiseList = someList.map( item => this.fireLeMissles(item));
    return Promise.all(promiseList);
  }).then(childPlacesIds => {
    childPlacesIds.forEach(childPlacesId = {
      // .... do something;
    });
    // return something.
  }).catch(err => console.log("Yea, life sucks sometimes."));

}

P. S: I am not sure where this param, someList, childPlacesIds is coming from, and assumed that you are initializing that promiseList as empty array.

Mido's answer is good, I just wanted to provide an alternative take on it which I think would be useful to know - using promises for the proxies they are:

Killmyself.prototype.fireLeMissles = function () {
  let fn = this.anotherFunction(param);
  let others = fn.then(_ => someList.map(this.fireLeMissles, this));
  let othersP = Promise.all(others);
  othersP.then(/* do OtherStuff */);
  return othersP; // or whatever its then returned
}

Of course, this gets even easier with a library like bluebird.

发布评论

评论列表(0)

  1. 暂无评论