Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Repeatedly prompt user until resolved using nodeJS async-await

I try repeteadly asking question to user until they gave the correct answer using this code.

The problem is, it won't resolve if the user doesn't gave the right answer at the first time.

var readline = require('readline');
var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

function promptAge() {
    return new Promise(function(resolve){
        rl.question('How old are you? ', function(answer) {
            age = parseInt(answer);
            if (age > 0) {
                resolve(age);
            } else {
                promptAge();
            }
        });
    });
}

(async function start() {
    var userAge =  await promptAge();
    console.log('USER AGE: ' + userAge);
    process.exit();
})();

Here are the terminal outputs for each condition:

When user gave the right answer the first time it was fine ...

How old are you? 14
USER AGE: 14

When user gave the wrong answer it was stuck (won't resolve and process won't exit) ...

How old are you? asd
How old are you? 12
_

When user doesn't gave any answer it was also stuck ...

How old are you? 
How old are you? 
How old are you? 12
_

Could anyone explain what happened or give me any reference to an article/video that explain this nature?

Btw, i try to do this using async/await for learning purpose (trying to learn how to do things asynchronously). I already tried this without async/await (promptAge() doesn't return any promise) and it was okay.

Thank you for your attention.

like image 807
sirnobrain Avatar asked Aug 18 '17 18:08

sirnobrain


2 Answers

It's nothing to do with parseInt() although skellertor is advising good practice.

The problem is that you're generating a new Promise every time promptAge() is called - but the original caller i.e. start() only has visibility of the first Promise. If you enter a bad input, promptAge() generates a new Promise (inside a never-resolved Promise) and your success code will never run.

To fix this, only generate one Promise. There are more elegant ways to do this but for clarity, and to avoid hacking the code into something unrecognisable...

var readline = require('readline');
var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

// the only changes are in promptAge()
// an internal function executes the asking, without generating a new Promise
function promptAge() {
  return new Promise(function(resolve, reject) {
    var ask = function() {
      rl.question('How old are you? ', function(answer) {
        age = parseInt(answer);
        if (age > 0) {
          // internal ask() function still has access to resolve() from parent scope
          resolve(age, reject);
        } else {
          // calling ask again won't create a new Promise - only one is ever created and only resolves on success
          ask();
        }
      });
    };
    ask();
  });
}

(async function start() {
    var userAge =  await promptAge();
    console.log('USER AGE: ' + userAge);
    process.exit();
})();
like image 98
cortexlock Avatar answered Nov 13 '22 17:11

cortexlock


It looks like it has to do with your parseInt() function. In both cases you are passing a non-Number value. First check if it is a number before parsing it into an int.

like image 35
skellertor Avatar answered Nov 13 '22 17:11

skellertor