Search code examples
javascriptnode.jsmqtthandler

MQTT client for Node.js functions outside the handler but not inside


I'm trying to implement a series of transport providers following a provider architecture model. Therefore, I created an abstract transport provider, which is then extended by specific providers, such as mqtt, nats, etc. However, I'm having trouble with initializing an mqtt client. The code for initialising, connecting to and then interacting with the client, when put outside the handler, functions correctly. However, the same exact code inside a provider method connect() does not function as intended (it seems to initialize the client when watching with debugger and then skips through all the client.on functions).

This is the abstract provider, which simply outlines the methods that need to be used:

class abTransProvider {

    constructor() {

        this.client;
        this.host;
        this.username = "guest";
        this.password = "guest";

        if (this.constructor == abTransProvider) {
            throw new Error("Abstract classes cannot be instantiated.");
        };
    };

    init() {
        throw new Error("Abstract method is not implemented");
    };

    connect(host) {
        throw new Error("Abstract method is not implemented");
    };



};

module.exports = {
    abTransProvider: abTransProvider
};

This is the code for the mqtt handler itself. Note that this is just a basic skeleton, since I'm only testing the connection method now (for which there are only return values for connection and error):

const mqtt = require("mqtt");
const abTransProvider = require("../abTransProvider.js");

class specProvider extends abTransProvider.abTransProvider {
  
init() {

  }

  connect() {
    this.host = "mqtt://localhost:1883";
   
    this.client = mqtt.connect(this.host, {
      username: this.username,
      password: this.password,
    });

    // Mqtt error callback
    this.client.on("error", (err) => {
      console.log(err);
      this.client.end();
      return 1;
    });

    // Connection callback
    this.client.on("connect", () => {
      console.log(`MQTT client connected`);
      return 0;
    });

    // MQTT subscriptions
    this.client.subscribe("value", { qos: 0 });

    // When a message arrives, log it
    this.client.on("message", function (topic, message) {
      console.log(message.toString());
    });

    this.client.on("close", () => {
      console.log(`MQTT client disconnected`);
    });
  }
}

module.exports = {
    specProvider: specProvider,
  };

This is a simple testing program that calls the connect method:

const trProv = require("mqtt.js");
const transport = new trProv.specProvider();

const trCheck = transport.connect();

if (trCheck !== 0) {
    console.log("Error when connecting to the provider")
    process.exit(1)
} else {
    console.log("Connection successful")
}

When the testing program calls the mqtt handler method connect(), it seems to instantiate the client but the connected flag is false. However, instead of logging an error (if there is one), the program simply skips through the client.on functions and, therefore, the return value is undefined. I would highly appreciate any feedback/comments/advice on how to resolve this! Thank you!


Solution

  • This code really doesn't do what you think is does.

    All the return statements are in the lambda functions you have passed to the event handlers, so they will have no effect on the connect() method. They just set the return value of the lambda (which will be ignored because event callbacks don't return anything).

    And as a resultconnect() ends without returning anything hence the undrfinded.

    Probably the best option here is to have connect() return a promise that can be resolved in the on('connected',...) handler or rejected in the on('error',...) handler (but error can get called later so you need to be careful there).