So how can we go even further? Well, there is something completely unique about web sockets which is very hard to do in a normal http server, and that is creating specific protocols of message exchanges, that must happen in the right order, have the right content, and perhaps build up state in between. For that, we can use a purpose built Hexnut library called hexnut-sequence.

Hexnut Sequence: A Simple Way to Create Client / Server Conversations

Let’s take a really contrived example, and imagine that we have defined an exchange that can take place between a client and a server.

The client sends “Hello, I am <name>”

The server sends back “Hello <name>, how is the weather?”

The client sends “The weather is <weather type>”

The server responds “OK <name>, enjoy this <weather type> day”

This is pretty simple, with only 4 messages exchanged, and no complex loops or logic. But perhaps you can see that if we want to write this in a way that adheres to four principles we described above, it might get tricky.

When you have to send and receive related but independent messages back and forth, especially when the exchange is stateful, how can you separate the concerns and preserve a predictable information flow?

Hexnut Sequence helps solves this problem. Let’s implement the example and see how it keeps the code clean:

sequence.uninterruptible(…) will create a middleware for Hexnut, just like like handle.message(…). What is different however, is that the functions you pass are generator functions instead of normal ones. We will see the reason why shortly, but notice that all the code inside the generator function (except for the yield keyword) is just normal code.

So let’s take a look at the yield keyword on line 7 — in hexnut-sequence, the yield keyword is used for a couple of things, but mainly to signal that we need to wait for a message to come in from the client. In that way, it’s a little bit like await, but it’s different because we are only going to continue running this function if the message that comes in passes the test we provided to .matchMessage().

Then we extract the name from the message and send the response to the client. After that, we match another message for the weather and send the final response.

You can do more than .matchMessage() with yield though:

.getMessage() will simply wait for the next message with no validation

will simply wait for the next message with no validation .assert(condition) will cause the sequence to break if the condition is not true

will cause the sequence to break if the condition is not true .await(promiseReturningFunction) will await a promise, and only continue when it has resolved.

If you’re wondering what the uninterruptible thing is all about, it turns out there are 2 kinds of back and forth exchanges you can have:

A sequence that starts, and then some other unrelated messages come in, but continues as soon as the next relevant message appears (interruptible) A sequence that starts, but immediately resets if an unrelated message comes in the meantime (uninterruptible)

So in the example above, you have to play out the sequence exactly. If you send something unexpected, you’ll have to start the whole thing again. In the next example, we will change this to be interruptible instead to show the difference.

Interruptible vs Uninterruptible

If you were a gamer growing up in the 90s you might recognise the new sequence as the legendary konami code. In order to see the easter egg message, the client must send all of those messages exactly, in order, with no interruptions. However, the greeting sequence we saw before is now interruptible, so we can start by sending the name, but then send the whole konami code. When we finally go back and send the weather, the sequence will pick up exactly where it left off!