1

I'm testing my code with node v8.9.4

I want to use class methods in my promises chain. But this fails with an error: (node:68487) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property 'attr' of undefined

const fsp = require('fs-promise');

class MyClass {
    constructor() {
        this.attr = 'value';
    }

    promiseMe() {
        console.log(this.attr);
    }

    main() {
        fsp.readdir('.').then(this.promiseMe);
    }
}

new MyClass().main();

So I try to use arrow functions as class methods. But defining an arrow function as a class method is syntactically not correct: Unexpected token =

promiseMe = () =>  {
    console.log(this.attr);
}

This works, but it is really ugly:

const fsp = require('fs-promise');

class MyClass {
    constructor() {
        this.attr = 'value';
        this.promiseMe = () => {console.log(this.attr);}
    }

    main() {
        this.promiseMe();
    }
}

new MyClass().main();

So how can I use class methods in promises?

There is another question regarding this topic: How to use arrow functions (public class fields) as class methods? Unfortunately this doesn't work with my node.js setup.

Matthias M
  • 8,008
  • 11
  • 67
  • 83
  • Arrow functions as methods won't get `this` properly set when they are invoked. – Pointy Dec 23 '18 at 14:02
  • 2
    Possible duplicate of [How to use arrow functions (public class fields) as class methods?](https://stackoverflow.com/questions/31362292/how-to-use-arrow-functions-public-class-fields-as-class-methods) – Chris G Dec 23 '18 at 14:05
  • *"I want to use class methods in my promises chain."* -> `somePromise.then(someClass.someMethod.bind(someClass))` for a "class method" or `somePromise.then(someInstance.someMethod.bind(someInstance))` for an "instance method" – Thank you Dec 23 '18 at 14:14
  • Note you could also use `fsp.readdir('.').then(x => this.promiseMe(x))` – Thank you Dec 23 '18 at 14:18

3 Answers3

2

Right this is because your context is incorrect within your Promise. One method is to bind this to your Promise context. In your example you would call it like fsp.readdir('.').then(this.promiseMe.bind(this));

Alternatively, if you're using this more often, you can bind it in your constructor:

this.promiseMe = this.promiseMe.bind(this)

This will bind it in your class so that you no longer need to bind every time you call!

ZekeDroid
  • 6,713
  • 4
  • 26
  • 57
0

An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

You are getting TypeError: Cannot read property 'attr' of undefined because this is not referencing your class instance.

Rob
  • 12,212
  • 4
  • 33
  • 51
0

It looks like you've already figured out what options you have when it comes to the class syntax. But I suggest that you ask yourself the whether the class syntax/mechanism is actually providing something you need.

It may well be the case that you can accomplish everything you need by using a factory function. This avoids all the ugly quirks of using this:

const fsp = require('fs-promise');

function myClass() {
    let attr = 'value';

    const promiseMe = () => console.log(attr);
    const main = () => fsp.readdir('.').then(promiseMe);

    return { promiseMe, main };
}

myClass().main();
JLRishe
  • 90,548
  • 14
  • 117
  • 150