1

I'm trying to set a class member variable from a callback of a function I'm calling from the class constructor.

To be a bit more specific: I need to set the connection ID in the Connection class constructor based on the Redis INCR result (each client has a 'global' connection ID so I can have multiple nodes).

Here's the code.

class Connection {
  constructor() {
    client.incr('conn_id', (err, reply) => {
      this.connID = reply;
    });
  }
}

var lovely = new Connection();
console.log(`lovely connID is ${ lovely.connID }`);

This is the result: lovely connID is undefined

  • 3
    Not familiar with Redis but is `client.incr` asynchronous? That could mean you are running the console.log line before the callback to client.incr has had a chance to run. – rom99 Jun 24 '16 at 16:39
  • See http://stackoverflow.com/questions/34959257/why-isnt-my-future-value-available-now. –  Jun 24 '16 at 18:37
  • Your problem is not that you "can't set class member variable from callback", but that you "can't access class member variable before it's set". –  Jun 24 '16 at 18:38

3 Answers3

0

It seems that client.incr('conn_id' ....) is async , which means the callback will be invoked after your code run .

So

console.log(lovely connID is ${ lovely.connID }); will be called before the callback

(err, reply) => { self.connID = reply; }

which is similar to this :

class Connection{
  constructor(){
    self=this;
    setTimeout( function(){self.client='somevalue';
                          console.log('value1');}, 10)

  }
}

var a = new Connection();

console.log(a.client); 

running this will result

undefined value1

Bassam Rubaye
  • 302
  • 1
  • 4
  • Sure, so how should he fix it? –  Jun 24 '16 at 18:34
  • if he needs to log it , he should do that inside the callback , and if he needs to do more things he can call a function inside the callback – Bassam Rubaye Jun 24 '16 at 19:21
  • I wasn't asking for myself; I was suggesting that your answer was missing this part and should be amplified. Logging from inside the constructor isn't that great. What if I want to log from outside the constructor? –  Jun 24 '16 at 19:34
0

As others here have mentioned, the issue seems to be that client.incr is asynchronous and your code does not wait for it to resolve before accessing the properties. To remedy this issue, you could try passing in an onReady callback to Connection to enssure the properties will be there before trying to access them. Something along these lines:

'use strict';

// mock client.incr
var client = { incr: (id, callback) => {
  setTimeout(() => callback(null, 'Hello World'), 0) 
}};

class Connection {
  // receive an "onReady" function
  constructor(onReady) {
    client.incr('conn_id', (err, reply) => {
      this.connID = reply;
      // call "onReady" function, and pass it the class
      if (typeof onReady === 'function') onReady(this)
    });
  }
}

new Connection(lovely => { console.log(lovely.connID) })

I hope that helps!

m-a-r-c-e-l-i-n-o
  • 2,622
  • 18
  • 26
  • We are still using callbacks in this age of Promises? What if he wants to access the `connId` outside the constructor; how will he know if it's ready or not? –  Jun 24 '16 at 19:36
  • @torazaburo I saw it as a straight forward way to demonstrate what was wrong and how the sequence of events happen -- not a final implementation (thus the phrase: "... Something along the lines of ..."). If OP wants to use promises after he/she understand the data flow, then so be it, and he/she can use your answer as a guide. Thank you for providing that alternative. – m-a-r-c-e-l-i-n-o Jun 24 '16 at 20:21
0

In general putting heavy initialization logic in a constructor, especially if it's asynchronous, is not a good idea. As you've found, the constructor has no way to return the information about when the initialization is finished. An alternative is to create a promise for when the connection is ready. Then, in your outside code, you can hang a then off the property to specify the code you want to trun when it's ready.

class Connection {
  constructor() {
    this.connId = new Promise((resolve, reject) =>
      client.incr('conn_id', (err, reply) => {
        if (err) return reject(err);
        resolve(reply);
    });
  }
}

var lovely = new Connection();
lovely.connId . then(connId => console.log(`lovely connID is ${ connID }`);