In some earlier posts, here and here, I wrote about how you can use Node.js to write MQ programs. In this post, I’ll show how a feature of the language, Promises, makes it easier to write those MQ programs.
Node.js programs are built from asynchronous blocks or functions. Many operations are done by initiating some work and then defining a (callback) function to be invoked once the work has completed in the background. The MQI layer is no different. Take a look at the amqsput sample program for an example of how the callbacks get executed.
This can be a very powerful approach, but it can also lead to very convoluted code. In fact, it’s often been called “callback hell“. It is easy to end up with multiple layers of nested operations, and a string of closing braces (some with semi-colons). In the amqsput sample, I tried not to get too deep into the nesting; the
Put is in a separate top-level function called putMessage. But it can still be difficult to see the real structure and flow of the program.
Especially when the flow needs to be inherently synchronous as parts of the MQ programming model are. You cannot call MQPUT until you have done an MQOPEN. And you cannot call MQOPEN until you have done an MQCONN.
Promises are a mechanism that can make it much easier to code this kind of serial operation. I’m not going to write huge amounts of detail about how Promises work or a formal description of the interfaces. One good write-up is in this article. For our purposes, we just need to know there are two pieces of syntax to help – .then()and .catch(). Those names should already give an indication of what they try to achieve.
Promises and MQ
In a recent update to the MQI library for Node.js, I added some Promise-based functions for the most commonly-used verbs where pseudo-synchrony could be useful.
The new forms of the verbs all have the Promise suffix, to distinguish them from the truly-synchronous verb variants and the (default) async versions. So to open a queue, you now have the option of calling any one of
OpenPromise depending on which style of program you want. For example, in some of the sample programs I may call an
xxxxSync function even though that is not recommended in production applications simply because it makes it easier to concentrate on the aspect of the sample that I was trying to demonstrate.
The Promise-based flow can now look more like this pseudo-code.
ConnxPromise() .then(return OpenPromise()) .then(return PutPromise()) .then(return ClosePromise()) .then(return DiscPromise()) .catch(err)
That looks a lot cleaner, even if the actual function is identical to other programs.
Most of the asynchronous MQI verbs in this implementation return a single value to demonstrate success (the hConn for example after a successful connection), as well as an error indicator. Those values are available to be passed down the chain.
For a complete working example of Promises, see the amqsputp sample program.
I created Promise-based functions for
- Conn, Connx, Disc
- Open, Close
- Put, Put1
You may notice that there’s not a Get in that list, which is deliberate. The behaviour of Get is inherently asynchronous and driving callbacks multiple times in this implementation, more like the
onMessage models in other languages. Although there is a
GetSync verb, it is really really really not recommended that you use it in production applications because it blocks everything else going on inside the Node.js environment while waiting for the message to arrive on a queue. While it might be possible to write a single-shot message receiver with a Promise wrapping the Get function, I thought that there could be too much variation in what people wanted to do for there to be a good prescriptive GetPromise method.
The slightly-odd verb in this set is Sub. In the callback version, Sub needs to pass back two values – object handles for the subscription and for the destination queue. These are given as separate parameters to the callback function. In the Promise format, only one parameter can be passed down and so those two values are built into a single object containing the hObj and hSub elements. It’s the same function but spelled slightly differently.
Using Promises can help to simplify your Node.js application programs, making them easier to write and easier to maintain. I hope you find these Promises useful.