Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I always terminate a NodeJs script with a timeout even if the event loop is occupied?

Is it possible to use setTimeout in NodeJS to terminate a process even if the event loop is being occupied by something else?

For example, say I have code that looks like the following

setTimeout(async () => {
    // Some code I run to gracefully exit my process.
}, timeout);

while (true) {
    let r = 1;
}

The callback in my timeout will never be hit since the while loop will occupy the event loop. Is there some way that I can say: "Execute the following code after N seconds regardless of everything else?"

I'm writing selenium tests, but for some reason every once in a while the test will get "stuck" and never terminate. I basically want to always timeout my tests after a certain amount of time so we don't get into the position of tests that run forever.

Thanks!

like image 830
Vikram Sundaram Avatar asked Mar 07 '23 03:03

Vikram Sundaram


1 Answers

Since JavaScript is single threaded, what you are going to want to do is to create a worker using fork, which will give it the feeling of being multi threaded. This will actually just give us two instances of node, each one having their own event loop. This fork will have your endless loop which you can then kill with your timeout.

main.js

const cp = require('child_process')
const path = require('path')

// Create the child
const child = cp.fork(path.join(__dirname, './worker.js'), [])

// Kill after "x" milliseconds
setTimeout(() => {
  process.exit()
}, 5000);

// Listen for messages from the child
child.on('message', data => console.log(data))

Next you will setup your worker:

worker.js

let i = 0;
while (true) {
  // Send the value of "i" to the parent
  process.send(i++);
}

The child can communicate info about itself to the parent using process.send(data).

The parent can listen for messages from the child using child.on('message', ...).


Another thing we can do is kill the child instead of the main process if you need the main process to do more stuff still. And in this case you would call child.kill() inside the setTimeout instead.

const cp = require('child_process')
const path = require('path')

// Create the child
let child = cp.fork(path.join(__dirname, './worker.js'), [])

// Kill after "x" milliseconds
setTimeout(() => {
  child.kill()
}, 5000);

If there are no more events in the eventloop, the main process will automatically close itself thus we don't need to call process.exit().

like image 89
Get Off My Lawn Avatar answered Mar 14 '23 19:03

Get Off My Lawn