Search code examples
node.jscommand-line-interfacereadlinenodejs-stream

Node.js - MaxListenersExceededWarning - when writing a Node.js CLI application uisng readline module


I am trying to keep adding numbers in a loop as shown in the code below:

const readline = require('readline');

const askOnCommandLine = (question) =>
  new Promise((resolve) => {
    const p = readline.createInterface({
      input: process.stdin,
      output: process.stdout,
      prompt: question,
    });

    p.on('line', (input) => {
      resolve(input);
    });

    p.prompt();
  });

let counter = 0;

(async () => {
  while (true) {
    console.log('Loop:', counter);
    const number1 = await askOnCommandLine('Enter 1st number: ');
    const number2 = await askOnCommandLine('Enter 2nd number: ');
    console.log('Addition:', parseInt(number1) + parseInt(number2));
    counter += 1;
  }
})();

But there are two problems here:

  1. It prints a single key press multiple times as shown in the screenshot.

Loop: 1 Shows that first number is 222 but it actually just 2

  1. After a few loops, there an error as follows:
Enter 2nd number:
(node:706492) MaxListenersExceededWarning: Possible EventEmitter memory leak detected.
11 end listeners added to [ReadStream]. Use emitter.setMaxListeners() to increase limit
(Use `node --trace-warnings ...` to show where the warning was created)

Solution

  • Just like event listeners in the browser, you need to be dealing with these stream interfaces after you're done with them, otherwise they'll just keep building up in memory (hence the warning and the unexpected behavior). It looks like readline exposes a close() method, why not close the stream when you're done with it?

    const askOnCommandLine = (question) =>
      new Promise((resolve) => {
        const p = readline.createInterface({
          input: process.stdin,
          output: process.stdout,
          prompt: question,
        });
    
        p.on('line', (input) => {
          resolve(input);
          p.close();
        });
    
        p.prompt();
      });