2

In the bellow example, when I am running this code, calling the start function, only inside _one function this is defined. When continuing to the next function _two, this is undefined. Any explanation? And how to solve this? Thanks in advance.

'use strict';

class MyClass {
    constructor(num) {
        this.num = num;
    }

start() {
    this._one()
    .then(this._two)
    .then(this._three)
    .then(this._four)
    .catch((err) => {
        console.log(err.message);
    });
}

_one() {
    console.log('num: ' + this.num);
    return new Promise((resolve, reject) => {
        resolve();
    });
}
_two() {
    console.log('num: ' + this.num);
    return new Promise((resolve, reject) => {
        resolve();
    });
}
_three() {
    console.log('num: ' + this.num);
    return new Promise((resolve, reject) => {
        resolve();
    });
}
_four() {
    console.log('num: ' + this.num);
    return new Promise((resolve, reject) => {
        resolve();
    });
}
}


let myClass = new MyClass(4);
myClass.start();
Soren
  • 14,402
  • 4
  • 41
  • 67
Saro
  • 810
  • 2
  • 12
  • 22

4 Answers4

9

Modify the promises chain to this:

start() {
    this._one()
    .then(this._two.bind(this))
    .then(this._three.bind(this))
    .then(this._four.bind(this))
    .catch((err) => {
        console.log(err.message);
    });
}

The bind() will modify handlers to bind with the correct this object (the instance of MyClass). You will achieve the method invocation on MyClass instance.

Following your initial scenario, the Promise will invoke the handlers (_two, _three) as regular functions, which in Strict mode will have this as undefined.

See here more details about bind().

Dmitri Pavlutin
  • 18,122
  • 8
  • 37
  • 41
3

Your this inside the _two is undefined because you are loosing the lexical reference. You are passing a function has a callback to another function (the promise then). When this function is called you'll loose the this.

You can use bind to set the context of the this for the functions being called by the then:

.then(this._two.bind(this))

Using ES6 you can also use fat arrows to retain the this: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions

.then(() => this._two())

fmsf
  • 36,317
  • 49
  • 147
  • 195
3

I am personnaly using bluebird promises rather then native ES6. This promises perform faster (https://github.com/petkaantonov/bluebird/tree/master/benchmark), have more convinient API and available in both browsers and node.js (http://bluebirdjs.com/docs/getting-started.html) I am bind this value to the first promise in chain - after that your example works well

'use strict';
var Promise = require('bluebird');

class MyClass {
    constructor(num) {
        this.num = num;
    }

    start() {
        Promise.bind(this)
        .then(this._one)
        .then(this._two)
        .then(this._three)
        .then(this._four)
        .catch((err) => {
            console.log(err.message);
        });
    }

    _one() {
        console.log('num: ' + (this.num += 1));
        return new Promise((resolve, reject) => {
            resolve();
        });
    }
    _two() {
        console.log('num: ' + (this.num += 2));
        return new Promise((resolve, reject) => {
            resolve();
        });
    }
    _three() {
        console.log('num: ' + (this.num += 3));
        return new Promise((resolve, reject) => {
            resolve();
        });
    }
    _four() {
        console.log('num: ' + (this.num += 4));
        return new Promise((resolve, reject) => {
            resolve();
        });
    }
}


let myClass = new MyClass(4);
myClass.start();    

I've also changed methods slightly so that you can see progress in this.num

Vlad Ankudinov
  • 1,936
  • 1
  • 14
  • 22
0

You can use arrow functions and call the _two(), _three(), etc... inside them. Doing this, the this will be binded automatically.

this._one()
.then(() => { this._two() })
.then(() => { this._three() })
.then(() => { this._four() })
.catch((err) => {
    console.log(err.message);
});
Christian Benseler
  • 7,907
  • 8
  • 40
  • 71