I want to asynchronously process keyboard events in a Node.js application. It was recommended to me to use readline.emitKeypressEvents which leads to code like this:
import readline from "node:readline";
readline.emitKeypressEvents(process.stdin);
process.stdin.setRawMode(true);
process.stdin.on("keypress", (str, key) => {
if (key.name === "q") {
process.exit(0);
} else {
console.log(key);
}
});
So readline is told to send keypress events to stdin, stdin is switched to raw mode and then a keypress event listener is registered. When a key is pressed, it is logged to console. When Q is pressed the application exits.
What I really don't like about this approach is the destruction of this keyboard listener by explicitly terminating the application. I want to gracefully undo the stuff done by this code.
The keypress event listener can be unregistered with process.stdin.off
. The raw mode can be disabled with process.stdin.setRawMode(false)
but so far I was not able to find out how to tell readline to STOP emitting keypress events. And it looks to me that this part prevents Node.js from exiting on its own.
So here is a simple test case:
const listener = () => {};
// Start listening
readline.emitKeypressEvents(process.stdin);
process.stdin.setRawMode(true);
process.stdin.on("keypress", listener);
// Stop listening
process.stdin.off("keypress", listener);
process.stdin.setRawMode(false);
// TODO Tell readline to stop emitting keypress events to stdin
// Node.js should exit now on its own
So is there a way to tell readline to stop emitting keypress events to restore the original state so Node.js can exit without a manual call to process.exit
?
As usual right after writing my question I find the answer myself...
The documentation of emitKeypressEvents
says:
Closing the readline instance does not stop the input from emitting 'keypress' events.
This made me believe that this can't solve my problem. But it does. So the solution is creating a readline interface, passing it to emitKeypressEvents
and closing it when no longer needed:
import readline from "node:readline";
const readlineInterface = readline.createInterface({
input: process.stdin
});
const listener = () => {};
// Start listening
readline.emitKeypressEvents(process.stdin, readlineInterface);
process.stdin.setRawMode(true);
process.stdin.on("keypress", listener);
// Stop listening
process.stdin.off("keypress", listener);
process.stdin.setRawMode(false);
readlineInterface.close();
Now the process exits on its own without manually calling process.exit
.