I am wondering if there is a simple way to get "synchronous" readline or at least get the appearance of synchronous I/O in node.js
I use something like this but it is quite awkward
var readline = require('readline'); var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); var i = 0; var s1 = ''; var s2 = ''; rl.on('line', function(line){ if(i==0) { s1 = line; } else if(i==1) { s2 = line; } i++; }) rl.on('close', function() { //do something with lines })' Instead of this I would prefer if it were as easy as something like
var s1 = getline(); // or "await getline()?" var s2 = getline(); // or "await getline()?" Helpful conditions:
(a) Prefer not using external modules or /dev/stdio filehandle, I am submitting code to a code submission website and these do not work there
(b) Can use async/await or generators
(c) Should be line based
(d) Should not require reading entire stdin into memory before processing
210 Answers
Just in case someone stumbles upon here in future
Node11.7 added support for this doc_link using async await
const readline = require('readline'); //const fileStream = fs.createReadStream('input.txt'); const rl = readline.createInterface({ input: process.stdin, //or fileStream output: process.stdout }); for await (const line of rl) { console.log(line) } Remember to wrap it in async function(){} otherwise you will get a reserved_keyword_error
const start = async () =>{ for await (const line of rl) { console.log(line) } } start() To read an individual line, you can use the async iterator manually
const it = rl[Symbol.asyncIterator](); const line1 = await it.next(); 11Like readline module, there is another module called readline-sync, which takes synchronous input.
Example:
const reader = require("readline-sync"); //npm install readline-sync let username = reader.question("Username: "); const password = reader.question("Password: ",{ hideEchoBack: true }); if (username == "admin" && password == "foobar") { console.log("Welcome!") } 4You can just wrap it in a promise -
const answer = await new Promise(resolve => { rl.question("What is your name? ", resolve) }) console.log(answer) 1I think this is what you want :
const readline = require('readline'); const rl = readline.createInterface({ input: process.stdin , output: process.stdout }); const getLine = (function () { const getLineGen = (async function* () { for await (const line of rl) { yield line; } })(); return async () => ((await getLineGen.next()).value); })(); const main = async () => { let a = Number(await getLine()); let b = Number(await getLine()); console.log(a+b); process.exit(0); }; main(); Note: this answer use experimental features and need Node v11.7
2Try this. It's still not a perfect replication of a synchronous line reading function -- e.g. async functions still happen later, so some of your calling code may execute out of order, and you can't call it from inside a normal for loop -- but it's a lot easier to read than the typical .on or .question code.
// standard 'readline' boilerplate const readline = require('readline'); const readlineInterface = readline.createInterface({ input: process.stdin, output: process.stdout }); // new function that promises to ask a question and // resolve to its answer function ask(questionText) { return new Promise((resolve, reject) => { readlineInterface.question(questionText, (input) => resolve(input) ); }); } // launch your program since `await` only works inside `async` functions start() // use promise-based `ask` function to ask several questions // in a row and assign each answer to a variable async function start() { console.log() let name = await ask("what is your name? ") let quest = await ask("what is your quest? ") let color = await ask("what is your favorite color? ") console.log("Hello " + name + "! " + "Good luck with " + quest + "and here is a " + color + " flower for you."); process.exit() } UPDATE: implements it (source code here: ). It implements several other features as well, but they seem useful too, and not too overengineered, unlike some other NPM packages that purport to do the same thing. Unfortunately, I can't get it to work due to but I like its implementation of the central function:
function ask(questionText) { return new Promise((resolve, reject) => { readlineInterface.question(questionText, resolve); }); } 1Using generators your example would look like this:
var readline = require('readline'); var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); var i = 0; var s1 = ''; var s2 = ''; var iter=(function* () { s1 = yield; i++; s2 = yield; i++; while (true) { yield; i++; } })(); iter.next(); rl.on('line', line=>iter.next(line)) rl.on('close', function() { //do something with lines }) So yield here acts as if it were a blocking getline() and you can handle lines in the usual sequential fashion.
UPD:
And an async/await version might look like the following:
var readline = require('readline'); var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); var i = 0; var s1 = ''; var s2 = ''; var continuation; var getline = (() => { var thenable = { then: resolve => { continuation = resolve; } }; return ()=>thenable; })(); (async function() { s1 = await getline(); i++; s2 = await getline(); i++; while (true) { await getline(); i++; } })(); rl.on('line', line=>continuation(line)) rl.on('close', function() { //do something with lines }) In both of these "synchronous" versions, i is not used for distinguishing lines and only useful for counting the total number of them.
Here's an example but it requires reading entire stdin before giving results however which is not ideal
var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); function lineiterator() { var currLine = 0; var lines = []; return new Promise(function(resolve, reject) { rl.on('line', function (line){ lines.push(line) }) rl.on('close', function () { resolve({ next: function() { return currLine < lines.length ? lines[currLine++]: null; } }); }) }) } Example
lineiterator().then(function(x) { console.log(x.next()) console.log(x.next()) }) $ echo test$\ntest | node test.js test test Since I don't know how many strings you need I put them all in an Array
Don't hesitate to comment if you need a more detailed answer or if my answer is not exact :
var readline = require('readline'); var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); var i = 0; var strings = []; rl.on('line', function(line) { // 2 lines below are in case you want to stop the interface after 10 lines // if (i == 9) // rl.close() strings[i] = line i++ }).on('close', function() { console.log(strings) }) // this is in case you want to stop the program when you type ctrl + C process.on('SIGINT', function() { rl.close() }) 7The simplest (and preferred) option is available in the docs.
const util = require('util'); const question = util.promisify(rl.question).bind(rl); async function questionExample() { try { const answer = await question('What is you favorite food? '); console.log(`Oh, so your favorite food is ${answer}`); } catch (err) { console.error('Question rejected', err); } } questionExample(); 1We can use promise and process.stdin events together to simulate a synchronous input system
const { EOL } = require("os"); const getLine = async () => ( await new Promise((resolve) => { process.stdin.on("data", (line) => { resolve("" + line); }); }) ).split(EOL)[0]; const line = await getLine(); console.log(line);