Reading value from console, interactively

node.jsConsole

node.js Problem Overview


I thought to make an simple server http server with some console extension. I found the snippet to read from command line data.

  var i = rl.createInterface(process.stdin, process.stdout, null);
  i.question('Write your name: ', function(answer) {
	console.log('Nice to meet you> ' + answer);
	i.close();
	process.stdin.destroy();
	
  });

well to ask the questions repeatedly, i cant simply use the while(done) { } loop? Also well if the server receives output at the question time, it ruins the line.

node.js Solutions


Solution 1 - node.js

you can't do a "while(done)" loop because that would require blocking on input, something node.js doesn't like to do.

Instead set up a callback to be called each time something is entered:

var stdin = process.openStdin();

stdin.addListener("data", function(d) {
    // note:  d is an object, and when converted to a string it will
    // end with a linefeed.  so we (rather crudely) account for that  
    // with toString() and then trim() 
    console.log("you entered: [" + 
        d.toString().trim() + "]");
  });

Solution 2 - node.js

I've used another API for this purpose..

var readline = require('readline');
var rl = readline.createInterface(process.stdin, process.stdout);
rl.setPrompt('guess> ');
rl.prompt();
rl.on('line', function(line) {
    if (line === "right") rl.close();
    rl.prompt();
}).on('close',function(){
    process.exit(0);
});

This allows to prompt in loop until the answer is right. Also it gives nice little console.You can find the details @ http://nodejs.org/api/readline.html#readline_example_tiny_cli

Solution 3 - node.js

The Readline API has changed quite a bit since 12'. The doc's show a useful example to capture user input from a standard stream :

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.question('What do you think of Node.js? ', (answer) => {
  console.log('Thank you for your valuable feedback:', answer);
  rl.close();
});

More information here.

Solution 4 - node.js

I believe this deserves a modern async-await answer, assuming node >= 7.x is used.

The answer still uses ReadLine::question but wraps it so that the while (done) {} is possible, which is something the OP asks about explicitely.

var cl = readln.createInterface( process.stdin, process.stdout );
var question = function(q) {
    return new Promise( (res, rej) => {
        cl.question( q, answer => {
            res(answer);
        })
    });
};

and then an example usage

(async function main() {
    var answer;
    while ( answer != 'yes' ) {
        answer = await question('Are you sure? ');
    }
    console.log( 'finally you are sure!');
})();

leads to following conversation

Are you sure? no
Are you sure? no
Are you sure? yes
finally you are sure!

Edit. In order to properly end the input, call

cl.close();

at the end of the script.

Solution 5 - node.js

Please use readline-sync, this lets you working with synchronous console withouts callbacks hells. Even works with passwords:

var favFood = read.question('What is your favorite food? ', {
  hideEchoBack: true // The typed text on screen is hidden by `*` (default). 
});

Solution 6 - node.js

@rob answer will work most of the times, but it might not work as you expect with long inputs.

That is what you should be using instead:

const stdin = process.openStdin();
let content = '';

stdin.addListener('data', d => {
  content += d.toString();
});

stdin.addListener('end', () => {
  console.info(`Input: ${content}`);
});

Explanation on why this solution works:

addListener('data') works like a buffer, callback will be called when it is full or/and its the end of input.

What about long inputs? A single 'data' callback will not be enough, hence it you will get your input split in two or more parts. That is often not convenient.

addListener('end') will notify us when the stdin reader is done reading our input. Since we have been storing the previous data, we can now read and process it all together.

Solution 7 - node.js

Here's a example:

const stdin = process.openStdin()

process.stdout.write('Enter name: ')

stdin.addListener('data', text => {
  const name = text.toString().trim()
  console.log('Your name is: ' + name)

  stdin.pause() // stop reading
})

Output:

Enter name: bob
Your name is: bob

Solution 8 - node.js

I recommend using Inquirer, since it provides a collection of common interactive command line user interfaces.

const inquirer = require('inquirer');

const questions = [{
  type: 'input',
  name: 'name',
  message: "What's your name?",
}];

const answers = await inquirer.prompt(questions);
console.log(answers);

Solution 9 - node.js

This is overcomplicated. An easier version of:

var rl = require('readline');
rl.createInterface... etc

would be to use

var rl = require('readline-sync');

then it will wait when you use

rl.question('string');

then it is easier to repeat. for example:

var rl = require('readline-sync');
for(let i=0;i<10;i++) {
    var ans = rl.question('What\'s your favourite food?');
    console.log('I like '+ans+' too!');
}

Solution 10 - node.js

A common use case would probably be for the app to display a generic prompt and handle it in a switch statement.

You could get a behaviour equivalent to a while loop by using a helper function that would call itself in the callback:

const readline = require('readline');
const rl = readline.createInterface(process.stdin, process.stdout);

function promptInput (prompt, handler)
{
	rl.question(prompt, input =>
	{
		if (handler(input) !== false)
		{
			promptInput(prompt, handler);
		}
		else
		{
			rl.close();
		}
	});
}

promptInput('app> ', input =>
{
	switch (input)
	{
		case 'my command':
			// handle this command
			break;
		case 'exit':
			console.log('Bye!');
			return false;
	}
});

You could pass an empty string instead of 'app> ' if your app already prints something to the screen outside of this loop.

Solution 11 - node.js

My approach to this would be to use async generators.

Assuming you have an array of questions:

 const questions = [
        "How are you today ?",
        "What are you working on ?",
        "What do you think of async generators ?",
    ]

In order to use the await keyword, you must wrap your program into an async IIFE.

(async () => {

    questions[Symbol.asyncIterator] = async function * () {
        const stdin = process.openStdin()

        for (const q of this) {
            // The promise won't be solved until you type something
            const res = await new Promise((resolve, reject) => {
                console.log(q)

                stdin.addListener('data', data => {
                    resolve(data.toString())
                    reject('err')
                });
            })

            yield [q, res];
        }

    };

    for await (const res of questions) {
        console.log(res)
    }

    process.exit(0)
})();

Expected results:

How are you today ?
good
[ 'How are you today ?', 'good\n' ]
What are you working on ?
:)
[ 'What are you working on ?', ':)\n' ]
What do you think about async generators ?
awesome
[ 'What do you think about async generators ?', 'awesome\n' ]

If you want to get questions an answers altogether, you can achieve this with a simple modification:

const questionsAndAnswers = [];

    for await (const res of questions) {
        // console.log(res)
        questionsAndAnswers.push(res)
    }

    console.log(questionsAndAnswers)

   /*
     [ [ 'How are you today ?', 'good\n' ],
     [ 'What are you working on ?', ':)\n' ],
     [ 'What do you think about async generators ?', 'awesome\n' ] ]
   */

Solution 12 - node.js

I had to write a "tic-tac-toe" game in Node that took input from the command line, and wrote this basic async/await block of code that did the trick.

const readline = require('readline')

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

async function getAnswer (prompt) {
  const answer = await new Promise((resolve, reject) =>{
    rl.question(`${prompt}\n`, (answer) => {
      resolve(answer)
    });
  })
  return answer
}

let done = false
const playGame = async () => {
  let i = 1
  let prompt = `Question #${i}, enter "q" to quit`
  while (!done) {
    i += 1
    const answer = await getAnswer(prompt)
    console.log(`${answer}`)
    prompt = processAnswer(answer, i)
  }
  rl.close()
}

const processAnswer = (answer, i) => {
  // this will be set depending on the answer
  let prompt = `Question #${i}, enter "q" to quit`
  // if answer === 'q', then quit
  if (answer === 'q') {
    console.log('User entered q to quit')
    done = true
    return
  }
  // parse answer

  // if answer is invalid, return new prompt to reenter

  // if answer is valid, process next move

  // create next prompt
  return prompt
}

playGame()

Solution 13 - node.js

Blocking readline unblocked behaviour

Imagine you have three questions to be answered from the console, as you now know this code will not run because readline standard module has 'unblocked' behaviour say each rl.question is an independent thread so this code will not run.

'use strict';

var questionaire=[['First Question: ',''],['Second Question: ',''],['Third Question: ','']];

function askaquestion(question) {
const readline = require('readline');

const rl = readline.createInterface(
    {input: process.stdin, output:process.stdout}
    );
  rl.question(question[0], function(answer) {
    console.log(answer);
    question[1] = answer;
    rl.close();
  });
};

var i=0;  
for (i=0; i < questionaire.length; i++) {
askaquestion(questionaire[i]);
}

console.log('Results:',questionaire );

Running output:

node test.js
Third Question: Results: [ [ 'First Question: ', '' ],
  [ 'Second Question: ', '' ],
  [ 'Third Question: ', '' ] ]        <--- the last question remain unoverwritten and then the final line of the program is shown as the threads were running waiting for answers (see below)
aaa        <--- I responded with a single 'a' that was sweeped by 3 running threads
a        <--- Response of one thread

a        <--- Response of another thread

a        <--- Response of another thread (there is no order on threads exit)

The proposed solution uses an event emitter to signal the end of an unblocking thread, and includes the loop logic and end of program into its listener function.

'use strict';

var questionaire=[['First Question: ',''],['Second Question: ',''],['Third Question: ','']];

// Introduce EventEmitter object
const EventEmitter = require('events');

class MyEmitter extends EventEmitter {};

const myEmitter = new MyEmitter();
myEmitter.on('continue', () => {
  console.log('continue...');
  i++; if (i< questionaire.length) askaquestion(questionaire[i],myEmitter);    // add here relevant loop logic
           else console.log('end of loop!\nResults:',questionaire );
});
//

function askaquestion(p_question,p_my_Emitter) { // add a parameter to include my_Emitter
const readline = require('readline');

const rl = readline.createInterface(
    {input: process.stdin, output:process.stdout}
    );
  rl.question(p_question[0], function(answer) {
    console.log(answer);
    p_question[1] = answer;
    rl.close();
    myEmitter.emit('continue');    // Emit 'continue' event after the question was responded (detect end of unblocking thread)
  });
};

/*var i=0;  
for (i=0; i < questionaire.length; i++) {
askaquestion(questionaire[i],myEmitter);
}*/

var i=0;
askaquestion(questionaire[0],myEmitter);        // entry point to the blocking loop


// console.log('Results:',questionaire )    <- moved to the truly end of the program

Running output:

node test2.js
First Question: 1
1
continue...
Second Question: 2
2
continue...
Third Question: 3
3
continue...
done!
Results: [ [ 'First Question: ', '1' ],
  [ 'Second Question: ', '2' ],
  [ 'Third Question: ', '3' ] ]

Solution 14 - node.js

Easiest way is to use readline-sync

It process one by one input and out put.

npm i readline-sync

eg:

var firstPrompt = readlineSync.question('Are you sure want to initialize new db? This will drop whole database and create new one, Enter: (yes/no) ');

if (firstPrompt === 'yes') {
    console.log('--firstPrompt--', firstPrompt)
    startProcess()
} else if (firstPrompt === 'no') {
    var secondPrompt = readlineSync.question('Do you want to modify migration?, Enter: (yes/no) ');
    console.log('secondPrompt ', secondPrompt)
    startAnother()
} else {
    console.log('Invalid Input')
    process.exit(0)
}

Solution 15 - node.js

I have craeted a little script for read directory and write a console name new file (example: 'name.txt' ) and text into file.

const readline = require('readline');
const fs = require('fs');

const pathFile = fs.readdirSync('.');

const file = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

file.question('Insert name of your file? ', (f) => {
  console.log('File is: ',f.toString().trim());
  try{
    file.question('Insert text of your file? ', (d) => {
      console.log('Text is: ',d.toString().trim());
      try {
        if(f != ''){
          if (fs.existsSync(f)) {
            //file exists
            console.log('file exist');
            return file.close();
          }else{
            //save file
            fs.writeFile(f, d, (err) => {
                if (err) throw err;
                console.log('The file has been saved!');
                file.close();
            });
          }
        }else{
          //file empty 
          console.log('Not file is created!');
          console.log(pathFile);
          file.close();
        }
      } catch(err) {
        console.error(err);
        file.close();
      }
    });
  }catch(err){
    console.log(err);
    file.close();
  }
});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRisto NovikView Question on Stackoverflow
Solution 1 - node.jsrobView Answer on Stackoverflow
Solution 2 - node.jsMadhan GaneshView Answer on Stackoverflow
Solution 3 - node.jsPatrick.SEView Answer on Stackoverflow
Solution 4 - node.jsWiktor ZychlaView Answer on Stackoverflow
Solution 5 - node.jsArangoView Answer on Stackoverflow
Solution 6 - node.jszurfyxView Answer on Stackoverflow
Solution 7 - node.jsMiguel MotaView Answer on Stackoverflow
Solution 8 - node.jsDiogo CardosoView Answer on Stackoverflow
Solution 9 - node.jsRagnarok RagdollView Answer on Stackoverflow
Solution 10 - node.jszoran404View Answer on Stackoverflow
Solution 11 - node.jsAndrei GătejView Answer on Stackoverflow
Solution 12 - node.jsStefan MusarraView Answer on Stackoverflow
Solution 13 - node.jsvlc33View Answer on Stackoverflow
Solution 14 - node.jsRohit ParteView Answer on Stackoverflow
Solution 15 - node.jsniksolazView Answer on Stackoverflow