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

2

10 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(); 
11

Like 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!") } 
4

You can just wrap it in a promise -

const answer = await new Promise(resolve => { rl.question("What is your name? ", resolve) }) console.log(answer) 
1

I 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

2

Try 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); }); } 
1

Using 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.

0

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() }) 
7

The 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(); 
1

We 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); 

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy