user input with node.js

Jabba picture Jabba · Nov 7, 2015 · Viewed 8.1k times · Source

I have the following Node.js code that behaves strangely:

#!/usr/bin/env node

"use strict";

var readline = require('readline');

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

function input(prompt) {
    rl.question(prompt, function (x) {
        rl.close();

        console.log("debug: " + x);
        return x;
    });
}

function main() {
    var n = input("Number: ");
//    console.log("value: " + n);    // problematic line
}

main();

I want to mimic Python's raw_input, i.e. reading a line from the user. After showing the prompt, the program should be blocked until the user presses Enter.

If the "problematic line" is in comments, it works, the program is waiting for input. However, if this line is not in comments, then the program doesn't wait for input and n becomes undefined. Why? How to write a function that returns the user input?

Answer

Luis Sieira picture Luis Sieira · Nov 7, 2015

That's because you are expecting the execution of input wait until return is called, which is not how this will work. The problematic line is indeed the previous one. First, input does not return anything, the return statement is the return of the question callback function, but then, you seem to misunderstand the execution flow, as we all have at some point (you'll get it pretty quick after some dead-ends like this one)

  1. Your script is loaded
  2. You declare and define rl, input and main
  3. Main executes
  4. You define n as the result of input And here is where things start getting asynchronously funny
  5. since question is asynchronous, its execution start but does not block the process
  6. input returns undefined (while you're still waiting for the input)
  7. you print that undefined
  8. You write something on the input
  9. question() finishes its execution and calls the callback (the function you gave as second parameter)
  10. rl is closed
  11. the callback function returns the line, and it is swallowed by the void (this is not technical terminology, just a metaphor)

You may want to do it like this:

#!/usr/bin/env node

"use strict";

var readline = require('readline');

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

function input(prompt, callback) {
    rl.question(prompt, function (x) {
        rl.close();
        callback(x);
    });
}

function main() {
    var n = input("Number: ", console.log);
}

main();

If you're new to javascript and node, you may find very useful to use learnyounode and the node code school path or even, if you have the time, the money and the opportunity, read Node.js, MongoDB, and AngularJS Web Development, by Brad Dayley.