Vincent
Vincent

Reputation: 153

Nodejs, Mqtt: Connection and callback order is wrong?

I want to use the mqtt package for nodejs. Everywhere I look, I find code samples like this one (from official doc on github) :

const mqtt = require('mqtt')
const client  = mqtt.connect('mqtt://test.mosquitto.org')

client.on('connect', function () {
  client.subscribe('presence', function (err) {
    if (!err) {
      client.publish('presence', 'Hello mqtt')
    }
  })
})

client.on('message', function (topic, message) {
  // message is Buffer
  console.log(message.toString())
  client.end()
})

My problem is that the connection: mqtt.connect() is before the callback: client.on('connect', ...). I think it is the wrong order. From what I understand, we send a CONNECT packet, then declare what to do when receiving a CONNACK packet.

This is different from the python package where you instanciate a client, then you declare "on connect" and other callbacks, and only then you use a method client.connect(). From what I understand, you declare what to do when receiving a CONNACK packet, then send a CONNECT packet.

Is there a way to guarantee the correct order (like in the python package) ?

Edit : The main concern is that if the CONNACK message comes back very quickly, and the code to declare the "on connect" callback is inside some complicated conditions, the "on connect" callback could not trigger (because the connection is already done) and there will be no client.subscribe, so our client will not be subscribed to the intended topic.

I guess it is theorically possible but practically impossible ? But what if the code is a bit bigger ? I actually played with this a while ago and had a code where sometimes the subscription was made, and other times the subscription was missed, depending on the speed of the CONNACK response I guess. (The code just waited a certain amount of time, maybe 0.1seconds, before declaring the "on connect" callback)

Upvotes: 0

Views: 495

Answers (2)

Rituraj Borpujari
Rituraj Borpujari

Reputation: 463

You kind of guessed it right here

I guess it is theorically possible but practically impossible

Just that it's theoretically impossible too in Javascript.

See, the connect method connects to the MQTT server (test.mosquitto.org in our case). And its a IO call which will be executed asynchronously as Non-Blocking IO call

So what happens is this,

  1. Connection to the MQTT server is triggered in a Non-Blocking IO way
  2. Since the IO call is non-blocking, it will move to the next line (registering the connect callback)
  3. the connection establishment event will be received by the callback registered to connect event

So the JS event loop (read more here) will never process the connection established event before registering the connect callback, thus ensuring the code always works.

Upvotes: 1

Dm Prkp
Dm Prkp

Reputation: 110

I don't know what is write on python pack, but if we look on official Mqtt docks correct order is:

  1. connect to mqtt server
  2. then send command to mqtt server that your client subscribed to some topics
  3. and then on our Node layer we add some callback (client.on('message', console.log)) to handle incoming messages

Edit 1 client.on('message', console.log) or client.on('connect', console.log) will work immediately when events was published by EventEmitter

Edit 2 i will try to describe the 3 points above more verbosely using your code example

// try connect to Mqtt server (point 1)
const client  = mqtt.connect('mqtt://test.mosquitto.org')

// when connection is done
client.on('connect', function () {
// try to subscribe to some topic (point 2)
  client.subscribe('presence', function (err) {
    if (!err) {
      client.publish('presence', 'Hello mqtt')
    }
  })
})

// when we got some message from mqtt we call a callback function (point 3)
client.on('message', function (topic, message) {
  console.log(message.toString())
  // and close the client
  client.end()
})

Edit 3 Declaration of "on connect" callback, will be faster than connection. This mechanic is called micro and macrotasks. Try this example in console:

setTimeout(() => console.log('connection'), 0)
console.log('add callback')
console.log('add callback')
console.log('add callback')
console.log('add callback')
console.log('add callback')

Upvotes: 2

Related Questions