Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read user input Node.js - Refactor with Promises

Please read this very short article to get up to speed on what I am trying to do.

the main point is that, as the author says, I "need to ask the user several questions, ...validate the input data and re-ask questions when something was entered wrong." Furthermore, I might need to do more complex things to validate the input, aside from checking it against a regular expression. Like, for example, check to make sure the current input wasn't entered by the user at an earlier prompt.

I am interested in refactoring the main code snippet from this article so that it is promise-based. The obvious reason why I want to do this is to avoid callback hell in the event that i need to prompt the user more than two or three times. Do you have any suggestions on how to go about refactoring this? The thing that is tripping me up is the recursive call to ask(), and also the fact that if a promise fails, thats it: its done. Whereas this code needs to loop over and over again until it gets valid input. SO I don't even really know where to start.

Also, if you have any "gordian knot"-type solutions, I am open to going in a completely different direction. Maybe Node has a built-in library for doing this kind of stuff that I am not aware of? Maybe readline? I don't know though cuz I am too new to this stuff and the readline API doesnt really make much sense to me...

Basically, if you can think of ANY way to refactor this so that I don't have to go deep into a callback nightmare, I would be grateful :)

Anyway, here is the code snippet in full:

function ask(question, format, callback) {
 var stdin = process.stdin, stdout = process.stdout;

 stdin.resume();
 stdout.write(question + ": ");

 stdin.once('data', function(data) {
   data = data.toString().trim();

   if (format.test(data)) {
     callback(data);
   } else {
     stdout.write("It should match: "+ format +"\n");
     ask(question, format, callback);
   }
 });
}
like image 336
Jeff B Avatar asked Oct 16 '25 22:10

Jeff B


2 Answers

At first, promisify the stdin reader:

function askOnce(question){
  var stdin = process.stdin, stdout = process.stdout;

  stdin.resume();
  stdout.write(question + ": ");

  return new Promise(res => {
    stdin.once('data', function(data) {
      res(data.toString().trim());
    });
  });
}

So no asking is easy:

async function ask(question, format){
  let answer;

  do {
    answer = await askOnce(question);
  } while(!answer.test(format));

  return answer;
}

Without async / await its a bit uglier (using recursion and the promise flattening mechanism):

 function ask(question, format){
   return askOnce(question).then(answer =>  {
      if(!answer.test(format))
         return ask(question, format);
      return answer;
  });
}
like image 120
Jonas Wilms Avatar answered Oct 18 '25 11:10

Jonas Wilms


are you reinventing the wheel? why not use the prompt module? this takes user input from the command prompt in a promise based manner.

https://www.npmjs.com/package/prompt

like image 20
danday74 Avatar answered Oct 18 '25 12:10

danday74