Getting an undefined "this" when using Classes/Async Await [duplicate]
Asked Answered
B

1

13

I've just started to experiment with classes and async await. I'm using Node version 8.9.0 (LTS). When I console.log(this), I get undefined instead of the reference to the object.

subhandler.js

class Handler {
  constructor(props) {
    this.defaultRes = {
      data: successMessage,
      statusCode: 200
    };
  }

  async respond(handler, reply, response = this.defaultRes) {
    console.log(this); // why is `this` undefined????
    try {
      await handler;
      return reply(response.data).code(response.statusCode)
    } catch(error) {
      return reply(error);
    }
  }
}

class SubHandler extends Handler {
  constructor(props) {
    super(props);
    this.something = 'else';
  }

  makeRequest(request, reply) {
    console.log(this); // why is `this` undefined!!
    // in this case, doSomeAsyncRequest is a promise
    const handler = doSomeAsyncRequest.make(request.params);
    super.respond(handler, reply, response);
  }
}

module.exports = new SubHandler;

Inside Hapi route

const SubHandler = require('./subhandler');

server.route({
    method: 'GET',
    path: '/',
    handler: SubHandler.makeRequest,
    // handler: function (request, reply) {
    //  reply('Hello!'); //leaving here to show example
    //}
});

Prototype example

function Example() {
  this.a = 'a';
  this.b = 'b';
}

Example.prototype.fn = function() {
  console.log(this); // this works here
}

const ex = new Example();
ex.fn();
Barby answered 14/11, 2017 at 16:28 Comment(5)
How are you calling makeRequest?Offcenter
It is called from a Hapi route handler hapijs.com/tutorials/routingBarby
For this kind of issue, usually there's a .bind(this) missing in the call.Polytrophic
Sounds like a basic JS this issue. Like here and here and here and here and many other questions probably.Loveinidleness
async/await is part of ES2017, not ES7 (ES2016)Petronia
O
14

If you want this to always point to the instance in makeRequest, bind its context in the constructor:

class SubHandler extends Handler {
  constructor(props) {
    super(props);

    this.makeRequest = this.makeRequest.bind(this)

    this.something = 'else';
  }

  makeRequest(request, reply) {
    console.log(this);
    const handler = doSomeAsyncRequest.make(request.params);
    super.respond(handler, reply, response);
  }
}
Offcenter answered 14/11, 2017 at 16:45 Comment(4)
Hm, that works, but am I missing something here? I figured that this would work out-of-the-box using the new ES6 class keyword. So basically, for all the methods of the class to get a reference to this I'd have to bind its context in the constructor?? Seems oddBarby
No, classes do not autobind their member functions in JavaScript.Offcenter
class is mostly just syntactical sugar over the prototype-based inheritance that JS has had for ages. this semantics and most other behaviour is still the same.Loveinidleness
So to be clear, we need to manually bind this to every method of a class ?Filigreed

© 2022 - 2024 — McMap. All rights reserved.