This is a node question, not NR, how do you do a local script blocking wait, but not block the node event loop?

Figure everyone in this forum is very 'node' savvy, so that is why I am asking here. I am trying to drive a MAX7219 controlled display. Writing an updated node library for it. I have it done but for one basic problem. I need to write to the display, wait a specific time, and write again, etc. This is to support a ticker-tape mode node. This has to be a sync stream of writes and waits, it can't be async. To using setInterval, setTimeout, promise methods don't seem to work, using await cannot be used. Every time my code writes to the display for the first message, it works, but the next message write steps on it, because the sync blocking is really async based. Got to love node/JavaScript for this one!

For example...

node input 1... write to display... right to left shifting one character at a time, so write wait write wait etc...
Buffer is...
"012345678 This is o test!"

Display...
" "
Wait...
Display...
" 0"
Wait...
Display...
" 01"
Wait...
Display...
" 012"
Wait...
Display...
" 0123"

I trust you get the idea?

No matter what method I have tried, timeout, promise, interval, etc. The inputs overlap, by whatever the initial delay offset is between the first and second input.

I can't use sleep, per my understanding because it blocks the entire node js event loop? But the basic sleep module works, my MAX7219 write routine does not return until the entire first message is displayed correct.

I have spent several hours reading on Google, trying different methods, but non seeme to do a true current thread block quite the way I need it. Or I am just not implementing the various methods right?

Hope someone with more node js (JavaScript) knowledge knows of a method to do a sync wait that actually works, that does not do something evil to anything else. Or maybe there is another way altogether that would work, I am just not finding or know of?

I realize this is a unique issue for node js given its core nature is async.

Oh, I tried using a trigger node in NR, but because my write routine returns too fast... the trigger fires to soon, so I get the next input message too fast, and I end up with the overlap again. Same with a delay or rate limit, either the rest comes to fast, or the total time is not known so rate limit does not work for this. I tried using simple queue, and a couple of other NR queue modules, but always come back to the fact that my write routine is coming back too soon, while the display is still working on the current message.

In what way doesn't setTimeout work? You can't guarantee that it will come back at the required time because there might be something else going on that delays it. It depends what sort of accuracy you need. But if it seems not to be working at all then I think it is going to be a problem with the code rather than with nodejs.

I am tripping up over the async versus sync scenario. My main thread returns before the timeout or delay completes, then the delay or timeout completes. So the actual timeout works but it is not blocking my main thread. I need my main thread to halt until the timeout triggers. As I understand it, this is something that node/javascript is just weak on in general because of its async oriented architecture.

It is an ugly hack, but I could just use a while loop, query time, and spin for the required time I need to wait, this would block my main thread, this would not block the node event queue (like the sleep module does per my understanding). But I am loath to use such a hack.

But you are also right, I may just need to rewrite the code, to avoid the issue in the first place. Oh, this is not a time critical scenario, actuate to a 100 milliseconds or so, is fine worst case. The refresh rate of the display as each character is moved from cell to cell is unreadable if it is faster than 100 ms, 200 or 250 ms is much easier on the eyes.

Blocking is bad. Node is inherently event based. So rather than thinking of node being "bad" at sync, you likely need to adjust your thinking to either yourself being bad at async or event based programming (no offence intended)

Sounds to me like you need a state machine. Search npm for state machine, there are a few. A state machine will permit you to perform an action then transition to the next state when a condition is met. All without a dirty hack of looping.
State machines can seem daunting at first however, give it a go, I think it might be a good fit for your current task.

2 Likes

I agree with @Steve-Mcl. It's not usually a good strategy to try fighting the async nature of JavaScript.

My first thought of a potential solution is a queue. I assume you have a function to write a single character on the screen? You could try creating an array of characters to be printed. Printing them would be controlled by a setInterval calling the character print function which would take its input by shifting the queue.

When a new message is receiced, you'd push its characters at the end of rhe character queue (array), then start the setInterval that calls the print character function until the queue is empty. When it reaches the end it should stop the interval. There should also be a variable indicating if the setInterval is running and only start it if it's not already running to prevent consecutive messages from overlapping.

Yup, I am thinking of the queue direction actually. When I created this same basic solution in python, I used a queue based model to avoid this issue. Since I have any number of MQTT based directives that will hit this device, I have to 'throttle' it in some way.

Right, need is FSA, a finite-state-automaton... (Yes, fancy name for a state machine).

The funny thing is... I looked at the Sleep node implementation, and it is using C# or C++ library to translate a 'nicer' wait, that is not as nasty to the node event queue. I am using it for now, just to let me work on the queue model. But at some point I will swing back round and re-enable the setInterval based character loop, I already have working. The other loop will be queue based, as discussed above. The wrinkle is the device driver is just another node library, and the NR wrapper is leveraging it as a canned module, so I am going to need to implement some type of notification back to the outer loop, i.e. queue, so the queue knows when the inner loop is done, again as noted above. But that should be straight-forward, even a simple return with some value gets that done.

Guys, as always, thanks. Really cool that the ideas that everyone suggests. This time is was more of a reality check than a design question, but just the same, really cool.

Implementing a queue is fairly easy, you can use an array and each time you get sent a character then push() it onto the array. Then check to see if your setTimeout is already running. If it is then that is it. If it isn't then call a function with shifts the next character off the array and initiates the send, and starts the timeout. In the timeout function check to see if there is anything in the array, if not then stop, otherwise call the function again to start the next one.

Yeah, I am testing the following right now...

  1. on input, I enqueue element to queue
  2. if setInterval not active, enable it after #1 step
  3. setInterval function, in function, unhooks by clearInterval, I do the work, drive the LED display, then hook the interval again and do the send() for the given input step #1
  4. if #3 processed the last item in the queue, I don't rehook the interval via setinterval so I am ready for the next input whenever that happens, but no interval loop running with an empty queue.

I also check to see if setinterval is active on a close event of the node, and if so unhook it via clear just to make I have cleaned up everything. Of course, if close comes, and the queue is not empty, the pending queue items are not handled and lost, but I deemed that ok, since if a close hits it likel does not matter what was left in the queue.

The above gives me FIFO sequence, per input message, and the setinterval at the end of step 3, makes sure I don't have anything left in the queue for longer than one loop of the interval task.

Personally I would use setTimeout rather than setinterval, so I have closer control of what is going on, and you don't have to wait for up to an interval to pass when there is no need. Either way will work though, whichever you find it easiest to get your head round is the way to go. This sort of stuff always stretches the brain, well mine at least, so whichever you find easiest to understand is most likely to result in a working solution :slight_smile:

Good idea, I will try that. I used setinterval because I already had it in the works. but avoid that first loop delay makes perfect sense. I am still learning node/javascript... given I used bash, powershell, python for years, some of the quirks with node, are... frustrating at times, but having fun.

You want to stretch the brain... try debugging C threading! It will give you nightmares. LOL.

Done that.

That's what I spend a lot of my working day doing :smiley: It's not really too bad if you have a decent IDE and debug environment that lets you examine and control the individual threads.

Threading's pretty much essential in any complex asynchronous system.

I learned C, on the Macintosh.... oh, 1985? the Apple API was a PITA, but... once you got code right, it was pretty solid, usually stable for a long time. Now, the PC, Windows 1.x, 2.x, 3.x... Talk about the wild, wild, west. So many different hardware vendors, no consistent frameworks, like years later as .NET emerged.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.