0

I'm trying to improve performance of processing my worker's incoming messages using a queue.

However, the sleep function takes anywhere between 16 to 30 milliseconds to complete instead of the 1 millisecond. Is there a better way to process the queue without this large delay, and without keeping my app at 100% cpu?

I am doing something like this:

var actions = new Queue();
parentPort.on('message', (msg) => actions.enqueue(msg));

loopy();

async function loopy() {
    while (true) {
        if (actions.size() > 0) {
            let action = actions.dequeue();
            //do work
            continue; 
        }

        await sleep(1);
    }
}

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

Any help would be appreciated. Thanks!

JoeOfTex
  • 97
  • 1
  • 10
  • Going back to the event loop (which is what `setTimeout()` does) has overhead associated with it because the event loop has other things to check besides just the next timer. So, there is no way to await for exactly 1ms in nodejs. What's more relevant here is WHY are you trying to wait exactly 1ms in a while loop? What are you hoping to accomplish with that? You should probably turn your question around and describe what you're trying to accomplish and then we can help with other/better ways to accomplish it than what you have now or trying to rely on timings that aren't designed for that job. – jfriend00 Jun 22 '21 at 05:49
  • Does this answer your question? [What is the reason JavaScript setTimeout is so inaccurate?](https://stackoverflow.com/questions/21097421/what-is-the-reason-javascript-settimeout-is-so-inaccurate) – Ken Y-N Jun 22 '21 at 06:52
  • @jfriend00 I would do this in other languages, but those were multithreaded, and it doesn't translate well to JS. I tried to solve two different problems (keep alive vs process action) in one step. I'm opting to use Ricky Mo's answer. The sleep was to avoid using 100% cpu with an infinite loop. – JoeOfTex Jun 22 '21 at 13:46

1 Answers1

1

while(true) is (usually) not a good idea. You should call the dequeue function after you enqueue a message. The dequeue function should end when 1. there is already a dequeue function running 2. no more message in the queue.

var isProcessing = false;
var actions = new Queue();
parentPort.on('message', (msg) => {
  actions.enqueue(msg)
  tryDequeue();
});


async function tryDequeue() {
    if(isProcessing || actions.size() == 0)
    {
      return;
    }
    isProcessing = true;

    let action = actions.dequeue();
    //do work
    
    isProcessing = false;
    tryDequeue();
}
Ricky Mo
  • 6,285
  • 1
  • 14
  • 30
  • I'll try this tomorrow and report back. I used the loop to avoid blocking on the message handler, but I see yours has the same effect without a sleep. I think I also needed to keep the worker alive, so I used a loop. – JoeOfTex Jun 22 '21 at 05:31
  • `while (true)` is perfectly OK if there's an `await` inside the loop. That's the one time it's OK. I think the bigger issue is why are they trying to sleep for 1ms. What is that accomplishing? Not much. – jfriend00 Jun 22 '21 at 05:46
  • I'm accepting this answer, because it solves the problem I was having by avoiding sleep altogether. Then, for keeping the worker alive, I can use a `set Interval` with a really long delay. – JoeOfTex Jun 22 '21 at 13:42