In the following code, I assign a listener to the data
event of process.stdin
with the once
method.
console.log('Press Enter to allow process to terminate')
process.stdin.once('data', callback)
function callback (data) {
console.log('Process can terminate now')
}
In theory, when the callback has fired, the event listener should be automatically removed (because I attached it with once
), allowing the process to terminate. Surprisingly, in this case, the process never terminates (The code you see is the whole thing, try it!). I also tried manually removing the listener, but that changes nothing.
Is there something else going on here that I don't realise perhaps?
Adding the data
event listener to process.stdin
add a reference to it that keeps the process open. That reference stays in place even after removing all event listeners. What you can do is manually unref()
it in your callback, like so:
console.log('Press Enter to allow process to terminate')
process.stdin.once('data', callback)
function callback (data) {
console.log('Process can terminate now')
process.stdin.unref()
}
Also, as a general debugging tool for stuff like this, there are two (undocumented) functions that you can call to get a list of things keeping your process open:
process._getActiveHandles()
process._getActiveRequests()
See this pull request in the node project for background.
Update: You asked about attaching event listeners after you've unref()
'd process.stdin
. Here's a quick example showing that the listener does attach itself and function:
console.log('Press Enter to allow process to terminate')
process.stdin.once('data', callback)
function callback (data) {
console.log('Unreferencing stdin. Exiting in 5 seconds.')
process.stdin.unref()
process.stdin.once('data', function(data) {
console.log('More data')
})
setTimeout(function() {
console.log('Timeout, Exiting.')
}, 5000);
}
With that code, if you press another key before the setTimeout
fires (5 seconds), then you'll see More data
output to the console. Once the setTimeout
's callback fires, the process will exit. The trick is that setTimeout
is creating a timer which the process also keeps a reference too. Since the process still has a reference to something, it won't exit right away. Once the timer fires, the reference it released and the process exits. This also shows that references are added (and removed) to things that need them automatically (the timer created by setTimeout
in this case).