Closed anirudhgiri closed 2 years ago
Hi, thank you for your feature request.
There is a Readline module in Node.js. You could try rl.question
to see whether it meets your need.
If it does not fit your need, feel free to share your view here please.
Regarding having a synchronous API, I don't think it would very useful now that we have support for top-level await
.
If https://github.com/nodejs/node/issues/37287 was implemented, you could do:
import { createInterface } from 'node:readline/promises';
const rl = createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'OHAI> '
});
const name = await rl.question("What's your name?");
console.log(`Hello ${name}!`);
If you wanted to implement it today, you can use util.promisify
:
import { createInterface } from 'node:readline';
import util from 'node:util':
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'OHAI> '
});
export const input = util.promisify(rl.question).bind(rl);
Hello @Ayase-252,
Thank you for pointing me to readline
. I would argue that importing a module, creating an Interface
object and linking it to process.stdin
and then using it's question
command, all for getting an input from stdin
seems is very beginner unfriendly.
I'm not saying there is no way to get user input from node, I'm saying that the way to do it is unnessecarily long and complicated for beginner programmers.
Getting user input in Python:
name = input("Enter your name")
Getting user input in C++:
cout << "Enter your name"
cin >> name
Meanwhile, getting user input in JS:
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question("What is your name?", name =>{
console.log(`Hello, ${name}!`);
rl.close();
})
The fact that you need all this boilerplate code for something as basic and imperative to a general purpose programming language as getting user input instead of just using one simple function like you do in Python is what I'm arguing against.
The solution @aduh95 provided wouldn't work either because you can only use promisify on functions that are (error,callback)
which rl.question
is not. You would need to use promisify.custom
, further complicating something that should be simple in the first place π.
The solution @aduh95 provided wouldn't work either because you can only use promisify on functions that are
(error,callback)
whichrl.question
is not. You would need to usepromisify.custom
, further complicating something that should be simple in the first place π.
rl.question
already has a promisify.custom
implementation, so util.promisify
works on it correctly.
https://nodejs.org/api/readline.html#readline_rl_question_query_options_callback
rl.question already has a promisify.custom implementation
Ah, apologies. I didn't know that, my bad. Everything else I raised still stands. It's too much boilerplate for a basic and frequently used functionality.
@anirudhgiri Hello
I'm neutral to implement the feature, but I would argue that complexity in some degree is unavoidable for new player in a new technology.
It reflects me as a completely naive learner without any prior knowledge about programming as a freshman in university. I was tought C as my first programming language. I have absolutely no idea about what #include <stdio.h>
is and does. The only thing I knew was it would not work without the magical #include <stdio.h>
if I want to print or input something to/from the gaint dark screen.
With C, the minimal CLI program will be something like
#include <stdio.h>
int main() {
char fav_food[100];
printf("What's your favorite food?");
scanf("%s", fav_food);
printf("That is your food" + fav_food);
return 0;
}
it would confuse a lot people who are new to programming from my experience. the #include
directive, library, memory allocate, pointer, format symbol etc. and Why we need return 0;
here.
As @aduh95 and @Linkgoron mentioned, thanks to awesome top-level await
, the eqivalent CLI program in Node.js can be
import utils from 'node:util';
import { createInterface } from 'node:readline';
const rl = createInterface({
input: process.stdin,
output: process.stdout,
});
const input = utils.promisify(rl.question).bind(rl);
const favFood = await input('What is your favorite food?\n');
console.log(`That is your food: ${favFood}`);
Still too complex? We can hide the readline
module behind module in userland say input
.
// input.mjs
import utils from 'node:util';
import { createInterface } from 'node:readline';
const rl = createInterface({
input: process.stdin,
output: process.stdout,
});
export const input = utils.promisify(rl.question).bind(rl);
In the learner side, the only thing she/he needs to do is import { input } from './input.mjs'
just like the magical #include <studio.h>
.
// main.mjs
import { input } from './input.mjs'
const favFood = await input('What is your favorite food?\n')
console.log(`That is your food: ${favFood}`)
Then run node main.mjs
β node git:(triaging/main) β node main.mjs
What is your favorite food?
Sushi!
That is your food: Sushi!
I think it is much simpler and explainable than a C example for new learners.
Edit: multiple grammar problems
Still too complex? We can hide the readline module behind module in userland say input.
// input.mjs import utils from 'node:util'; import { createInterface } from 'node:readline';
const rl = createInterface({ input: process.stdin, output: process.stdout, });
export const input = utils.promisify(rl.question).bind(rl); In the learner side, the only thing she/he needs to do is import { input } from './input.mjs' just like the magical #include
.
Excellent! Don't you think the short module you just wrote, input.mjs
, should already be available as a standard NodeJS function instead of relegating it to the user? Don't leave it up to the programmers to implement their own input.mjs
and instead have input()
be a part of Node's standard library because it is needed so frequently. Also the unnecessary complexity added from having to use the readline
module and createInterface
can be hidden away to make it easier for beginners to learn.
Still too complex? We can hide the readline module behind module in userland say input. // input.mjs import utils from 'node:util'; import { createInterface } from 'node:readline'; const rl = createInterface({ input: process.stdin, output: process.stdout, }); export const input = utils.promisify(rl.question).bind(rl); In the learner side, the only thing she/he needs to do is import { input } from './input.mjs' just like the magical #include
. Excellent! Don't you think the short module you just wrote,
input.mjs
, should already be available as a standard NodeJS function instead of relegating it to the user? Don't leave it up to the programmers to implement their owninput.mjs
and instead haveinput()
be a part of Node's standard library because it is needed so frequently. Also the unnecessary complexity added from having to use thereadline
module andcreateInterface
can be hidden away to make it easier for beginners to learn.
It's alright. Personally, I would perfer to incoperate input.mjs
into readline
module as static methods
to support usecase like
import { input } from 'node:readline'
const favFood = await input('What is your favorite food?\n')
console.log(`That is your food: ${favFood}`)
I think the implementation is simple. But I'm not sure about whether it is right to add one method input
on module readline
. I'd label with discuss
and readline
to see whether it is doable and there is other concern.
Another possibility could be to add a method to process.stdin
.
@targos
Thanks, I did some experiments around process
, but input(message)
may write to process.stdout
in order to display message
. It may introduce some coupling btw process.stdout
and process.stdin
.
In this case, could be process.input(message)
an option?
Hi @anirudhgiri and @Ayase-252,
What are your opinions about having that function named prompt
instead of input
?
window.prompt
does exist in the browsers and I thought it would be neat to have the same name, especially since more web (browser) APIs are coming to Node.js.
A quick check shows that Deno uses prompt
too.
Although I'm not sure whether it may be confused with .prompt()
method of readline instances.
@artembykov I like the idea, it would be ideal.
@artembykov Sounds good to me!
There has been no activity on this feature request for 5 months and it is unlikely to be implemented. It will be closed 6 months after the last non-automated comment.
For more information on how the project manages feature requests, please consult the feature request management document.
There has been no activity on this feature request for 5 months and it is unlikely to be implemented. It will be closed 6 months after the last non-automated comment.
For more information on how the project manages feature requests, please consult the feature request management document.
There has been no activity on this feature request and it is being closed. If you feel closing this issue is not the right thing to do, please leave a comment.
For more information on how the project manages feature requests, please consult the feature request management document.
I have just bumped into this while composing a beginner's programming course trying to use node/js as the first platform/language of a rookie. Will probably have to use process.argv
to get input in the initial lessons.
~Any counterarguments to using this?~
Buffer.concat(await process.stdin.toArray()).toString("utf-8")
EDIT: the counter-argument would be to use text
from the builtin stream/consumers module.
import { text } from "node:stream/consumers"
await text(process.stdin);
As I just discovered in https://github.com/whatwg/streams/issues/1019#issuecomment-902309992 β
Any counterarguments to using this?
Buffer.concat(await process.stdin.toArray()).toString("utf-8")
Assuming it works (I haven't tried), all the arguments in the discussion above:
async
function wrapper. Of course, anything that we could add will not work in older node versions.The hopes were that at least this feature would be available as a straightforward function from the get go.
Yeah, this is purely a "read all stdin", not the readline solution.
It's just the shortest/clearest incantation I've found (much better than using .on('data' | 'end')
handlers for this usecase).
- In older (LTS?) node versions it doesn't work without an
async
function wrapper. Of course, anything that we could add will not work in older node versions.
You have to go back to Node.js 12.x to find a version where top-level await
is not supported. The oldest non-EOL release line is 18.x at the time of writing.
The Problem
Javascript, used in conjunction with NodeJS, is increasingly being used as a general purpose programming language. While I acknowledge the fact that it was originally intended to be used as a language for server-side development, but the language has grown into something much more general from being used to create executables, GUIs, video games, mobile applications and even operating systems. The world of educational computer science is moving away from teaching beginners languages like C and Java as their first language to teaching them Python and NodeJS instead.
There are some features who's existence in a modern general purpose programming language is imperative like printing to the console, doing basic array and string operations, and getting user input. Unfortunately, getting user input from stdin through nodejs is an absolute pain. You should either declare a buffer, connect it to the
stdin
stream and use the buffer to read input line-by-line or use third party libraries.Having your users of your language, especially beginners, go through such a process just for the luxury of getting input from the user through the terminal for such a popular and widely used language should simply be unacceptable. Yet, it is the norm.
NodeJS is an available option in online learning platforms like HackerRank and Leetcode, and in hiring software used for coding interviews. Since most input is received through
stdin
, programmers either have to rely on the platform taking care of getting the input and just give the user a function with the inputs given as parameters to work with (like Leetcode does) or the programmers have to write all the boilerplate code to receive user input themselves (and loosing time and competitive edge in the process) like in Google Kickstart. This only drives people away from using Node in such competitive environments to other to use languages like Python (where they can just useinput()
) or C++ (where they can just docin >>
).The solution
Much like C's
scanf()
or Python'sinput()
, Node should have a simple and beginner friendly way to recieve user input from the terminal. My suggestion is two functions -input()
andinputSync()
.input()
andinputSync()
both return a line from stdin as a String but just likereadFile()
andreadFileSync()
,input()
does it asynchronusly whileinputSync()
does it synchronusly.Example Usage