I understand the rationale in Node.js for asynchronous events and I am learning how to write code that way. However, I am stuck with the following situation:
I want to write code that occasionally pauses for user input.
The program is not intended as a server (though currently it's intended for the command line). I realize this is an atypical use of Node. My goal is to eventually migrate the program back to a client-side Javascript application, but I find working in Node.js to be both fascinating and very useful for debugging. This brings me back to my example that illustrates the problem:
It reads in a text file and outputs each line unless the line ends with a "?". In that case, it should pause for user to clarify what was meant by that line. Currently my program outputs all lines first and waits for the clarifications at the end.
Is there any way to force node.js to pause for command-line input precisely for the cases where the conditional fires (i.e., where the line ends with a "?")?
var fs = require("fs");
var filename = "";
var i = 0;
var lines = [];
// modeled on http://st-on-it.blogspot.com/2011/05/how-to-read-user-input-with-nodejs.html
var query = function(text, callback) {
process.stdin.resume();
process.stdout.write("Please clarify what was meant by: " + text);
process.stdin.once("data", function(data) {
callback(data.toString().trim());
});
};
if (process.argv.length > 2) {
filename = process.argv[2];
fs.readFile(filename, "ascii", function(err, data) {
if (err) {
console.error("" + err);
process.exit(1);
}
lines = data.split("\n");
for (i = 0; i < lines.length; i++) {
if (/\?$/.test(lines[i])) { // ask user for clarification
query(lines[i], function(response) {
console.log(response);
process.stdin.pause();
});
}
else {
console.log(lines[i]);
}
}
});
}
else {
console.error("File name must be supplied on command line.");
process.exit(1);
}
Here's another way that has no dependencies (readline is built-in)
const readline = require('readline'); function askQuestion(query) { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise(resolve => rl.question(query, ans => { rl.close(); resolve(ans); })) } const ans = await askQuestion("Are you sure you want to deploy to PRODUCTION? ");
The trick is to not do it itteratively, but do the for loop recursively. So that the next line is printOut in a callback, that is called either A: after the line gets printed, or B: after the console input has been processed.
var fs = require("fs"); // modeled on http://st-on-it.blogspot.com/2011/05/how-to-read-user-input-with-nodejs.html function query(text, callback) { 'use strict'; process.stdin.resume(); process.stdout.write("Please clarify what was meant by: " + text); process.stdin.once("data", function (data) { callback(data.toString().trim()); }); } function printLinesWaitForQuestions(lines, someCallbackFunction) { 'use strict'; function continueProcessing() { if (lines.length) { printNextLine(lines.pop()); } else { someCallbackFunction(); } } function printNextLine(line) { if (/\?$/.test(line)) { // ask user for clarification query(line, function (response) { console.log(response); process.stdin.pause(); continueProcessing(); }); } else { console.log(line); continueProcessing(); } } continueProcessing(); } if (process.argv.length > 2) { var filename = process.argv[2]; fs.readFile(filename, "ascii", function (err, data) { 'use strict'; if (err) { console.error("" + err); process.exit(1); } var lines = data.split("\n"); printLinesWaitForQuestions(lines, function () { console.log('Were done now'); }); }); } else { console.error("File name must be supplied on command line."); process.exit(1); }
This is a good solution for two reasons:
I found a module that does this really easily for yes or no:
https://www.npmjs.com/package/cli-interact
Install: npm install cli-interact --save-dev
How to use is taken straight from the npm site:
var query = require('cli-interact').getYesNo;
var answer = query('Is it true');
console.log('you answered:', answer);
Here's the same answer as mpen, but without the confusing / unnecessary promise wrapper:
const readline = require('readline');
const rl = readline.createInterface({input: process.stdin, output: process.stdout});
rl.question('Press [Y] to continue: ', ans => {
if (ans == 'y') console.log('i will continue')
else console.log('i will not continue');
rl.close();
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With