28 Days - Debounce websocket messages
Today I’ll finish up the current string of websocket content that I have planned. I’ll be sharing a really neat technique that I have used in situations where I want to keep a frontend up to date with live content, but the live content comes in much faster than I want to update. In my situation, I desired a technique to “debounce” these messages. That is, I want to trigger 1 message every N seconds.
Special thanks to my co-worker Ben Olive for the idea of this technique. He is the brains behind a lot of cool techniques that have proven to me the power of Elixir.
Debouncing is a technique to limit the amount of requests made to a function in a limited amount of time. What I’ll demonstrate is leading edge debouncing. This means that the first message will immediately trigger, and every future event will occur 5s after the previous message. This all “resets” after 5 seconds without messages.
Here is a state diagram that Ben had put together awhile ago to demonstate how this can work:
The starting state is “idle”. When the function is called, the state enters “debouncing”, and also applies the function. If the function is called from this state, it enters a “called” state, but does not apply the function. From here, a X-second timeout will switch the state and apply the function. When the “debouncing” state is exited from a timeout, the entire state machine resets back to the initial.
Applied to Websockets
Now that there is a state diagram which represents what we need to code, the act of coding it and knowing it is correct becomes much simpler. I personally had a ton of trouble understanding what “debouncing” actually is, and having the reference made implementation much simpler.
I’ve put an entire implementation commit on github. There is a good bit of code in such a simple concept, but each bit of code is fairly small and relates to a step of the state machine diagram. Tests also accompany the implementation.
I don’t think that going in detail on the implementation will be much value, it is
fairly self contained. However, some of the testing is actually more interesting than
the debounce code. In particular, you’ll notice that I decided to setup the state machine
broadcast_from! rather than changing the state manually. I feel that this
gives the best “end usage” representation of the code, rather than an artifical test setup.
In addition, I do lay back on reading the socket state to get the updated assignment
values. There may be a better way to do this.
I hope you find this technique useful if you run into a similar use case. There may even be opportunity to turn something like this into a library which can be used more general purpose.
Thanks for reading the 12th post in my 28 days of Elixir. Keep up through the month of February to see if I can stand subjecting myself to 28 days of straight writing. I am looking for new topics to write about, so please reach out if there’s anything you really want to see!