8

I'm trying to use readline inside an else if statement:

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

rl.question("Would you like to see which cars are available? Please type yes/no: ", function(answer) {

    if (answer === 'yes') {
    // if yes do something
    } else if(answer === 'no') {
        rl.question("Would you like to search by latitude or longitude instead? If yes, please type latitude and longitude: ");
    } else {
        console.log("No worries, have a nice day");
    }

    rl.close();
});

What is the correct way to ask the user a different question if the user types 'No'?

Currently, if the user types No, the second question does not get asked.

Molda
  • 5,619
  • 2
  • 23
  • 39
Sara
  • 85
  • 1
  • 1
  • 5
  • I'm just wondering if you learnt what was wrong with your code since you accepted an answer that doesn't actually answer your question? – Molda Oct 12 '18 at 13:12

4 Answers4

9

If I were to do this I would start by making a promise based version of the readLine question function:

const question = (str) => new Promise(resolve => rl.question(str, resolve));

And I would structure it as a set of steps:

const steps = {
  start: async () => {
    return steps.seeCars();
  },
  seeCars: async () => {
    const seeCars = await question("Would you like to see which cars are available? Please type yes/no: ");
    if (seeCars === 'yes') { return steps.showCars(); }
    if (seeCars === 'no') { return steps.locationSearch(); }
    console.log('No worries, have a nice day');
    return steps.end();
  },
  showCars: async () => {
    console.log('showing cars');
    return steps.end();
  },
  locationSearch: async () => {
    const longlat = await question("Would you like to search by latitude or longitude instead? If yes, please type latitude and longitude: ");
    return steps.end();
  },
  end: async () => {
    rl.close();
  },
};

If you're new to async functions note that you have to type await before your question to instruct node not to proceed until the question resolves with an answer.

Also note that whenever we change steps you need to return so that the rest of the step doesn't run.

Here is the complete program for you to copy and play with:

const readline = require('readline');

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

// Create a promise based version of rl.question so we can use it in async functions
const question = (str) => new Promise(resolve => rl.question(str, resolve));

// A list of all the steps involved in our program
const steps = {
  start: async () => {
    return steps.seeCars();
  },
  seeCars: async () => {
    const seeCars = await question("Would you like to see which cars are available? Please type yes/no: ");
    if (seeCars === 'yes') { return steps.showCars(); }
    if (seeCars === 'no') { return steps.locationSearch(); }
    console.log('No worries, have a nice day');
    return steps.end();
  },
  showCars: async () => {
    console.log('showing cars');
    return steps.end();
  },
  locationSearch: async () => {
    const longlat = await question("Would you like to search by latitude or longitude instead? If yes, please type latitude and longitude: ");
    return steps.end();
  },
  end: async () => {
    rl.close();
  },
};

// Start the program by running the first step.
steps.start();
david
  • 17,925
  • 4
  • 43
  • 57
  • 1
    Thank you so much this is what I needed!! I'm fairly new to async functions but your example has helped in my understanding of using them – Sara Oct 12 '18 at 10:21
1

The rl.prompt() method writes the readline.Interface instances configured prompt to a new line in output in order to provide a user with a new location at which to provide input. Using setPrompt when the user typing 'no' to ask the different question.

const readline = require('readline');
let lastAnswer = '';
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: 'Would you like to see which cars are available? Please type yes/no: '
});
rl.prompt();
rl.on('line', (line) => {
    switch (line.trim()) {
       case 'yes':
           lastAnswer = 'yes';
           console.log('great!');
           rl.setPrompt('Would you like to see which cars are available? Please type yes/no: ');
           break;
       case 'no':
           if (lastAnswer === 'no') {
               lastAnswer = '';
               rl.close();
           }
           lastAnswer = 'no';
           rl.setPrompt('Would you like to search by latitude or longitude instead? If yes, please type latitude and longitude: ');
           break;
       default:
           lastAnswer = '';
           console.log(`Say what? I might have heard '${line.trim()}'`);
           break;
    }
    rl.prompt();
}).on('close', () => {
    console.log('Have a great day!');
    process.exit(0);
});
Pengcheng
  • 323
  • 1
  • 5
  • Thank you for your example, but if if the user types No for the second question, the statement will re ask the user the same question (latitude/ longitude), how can I allow the user to exit if he types in No twice? – Sara Oct 11 '18 at 19:49
  • you can use a variable saving the last answer, see my updates in the code. – Pengcheng Oct 11 '18 at 21:39
0

The question function requires a callback function otherwise it is ignored. So all you need to do to fix your problem is to add a callback function to the rl.question because that's the only way to get the answer and you want to know the answer right?

rl.question('...?', function(){});

https://github.com/nodejs/node/blob/master/lib/readline.js#L270

From Node.js source:

Interface.prototype.question = function(query, cb) {
  if (typeof cb === 'function') {
    if (this._questionCallback) {
      this.prompt();
    } else {
      this._oldPrompt = this._prompt;
      this.setPrompt(query);
      this._questionCallback = cb;
      this.prompt();
    }
  }
};
Molda
  • 5,619
  • 2
  • 23
  • 39
0

If you are not married to the readline package, [Inquirer][1] is a very nice npm library that supports use of promises. E.G.

var inquirer = require('inquirer');
inquirer.prompt([
  {
    type: 'confirm',
    name: 'whichCar',
    message: 'Which car do you want to drive?'
  }
]).then(function(response) {
   if (response.whichCar === true) {
      // do your thing
   } else { 
      // do your second prompt
   }
})

If you must used readline, then the above users have explained very well how the callbacks need to work. If not, then I think inquirer is a strong option with lots of flexibility and customization. [1]: https://www.npmjs.com/package/inquirer/v/0.3.5

henriquehbr
  • 1,063
  • 4
  • 20
  • 41
Adam Specker
  • 425
  • 3
  • 14