简体   繁体   中英

How to handle user inputs synchronously in Node.js

I know this may come across as a 'Noob Question', and I apologize in advance for asking something so simple. But I haven't been able to find much on this topic when searching online.

I'm trying to write a command line app in Node.js, however I haven't been able to figure out how to handle user input synchronously. I tried using async/await, but those seemed to have no effect. Instead I moved to promises, however the following code errors out with: TypeError: promise.resolve is not a function .

Reading the MDN it shows the Promise API as being new Promise(resolutionFunc, rejectionFunc) . With those functions being called on resolution and rejection respectively.

I'm not quite sure what I'm doing wrong, but from what I understand it should only be executing the .then() after the previous promise is resolved. However that is not the case here:

My Code:

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

let numbs = [];

function getInput(i) {
    let promise = new Promise((resolve) => {
        numbs[i] = resolve;
    }, (reject) => {
        // do nothing, shouldn't happen
    });

    rl.question(`Input #${i+1}: `, (input) => {
        promise.resolve(Number.parseInt(input));
    });

    return promise;
}

getInput(0)
.then(getInput(1))
.then(() => {
    console.log(numbs)
    /* 
        rest of the program using those 2 inputs 
    */
})
.catch((err) => {
    // do nothing, and exit
});

Output:

$ node .\file.js
Input #1: Input #1: 
[path removed]/file.js:18
        promise.resolve(Number.parseInt(input));    
                ^

TypeError: promise.resolve is not a function
    at [path removed]/file.js:18:17
    at Interface._onLine (readline.js:335:5)
    at Interface._normalWrite (readline.js:482:12)
    at ReadStream.ondata (readline.js:194:10)
    at ReadStream.emit (events.js:315:20)
    at addChunk (_stream_readable.js:296:12)
    at readableAddChunk (_stream_readable.js:272:9)
    at ReadStream.Readable.push (_stream_readable.js:213:10)
    at TTY.onStreamRead (internal/stream_base_commons.js:186:23)

There are several things wrong here:

  1. As you can see from the error, a regular promise does not have a .resolve() method and that isn't a correct way to wrap r1.question() in a promise.
  2. This isn't correct then(getInput(1)) . You need to pass a function reference to .then() so it can be called later.

Here's a way to fix those problems:

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

function getInput(i) {
    return new Promise(resolve => {
        rl.question(`Input #${i+1}: `, (input) => {
            resolve(+input);
        });
    });
}

let nums = [];

getInput(0).then(val => {
    nums.push(val);
    return getInput(1);
}).then(val => {
    nums.push(val);
    console.log(nums)
    /*
        rest of the program using those 2 inputs
    */
}).catch((err) => {
    console.log(err);
});

A simpler version uses async/await which are really convenient for serializing asynchronous operations:

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


function getInput(i) {
    return new Promise(resolve => {
        rl.question(`Input #${i+1}: `, (input) => {
            resolve(+input);
        });
    });
}

async function run() {
    let v1 = await getInput(0);
    let v2 = await getInput(1);
    console.log(v1, v2);
}

run().catch((err) => {
    console.log(err);
});

Note, your code isn't detecting if the user entered something other than a number and your code doesn't terminate the readline stream.

I was able to solve this in a different way than jfriend00 did above, instead of Promises I changed my code to use events and it works functionally the same as theirs. I'm pasting my code here for anyone interested, but I think they have a better answer.

const readline = require('readline');
const { EventEmitter } = require('events');
const events = new EventEmitter();
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    terminal: false
});

let numbs = [];

events.on("input0", () => {
    rl.question(`Input #1: `, (input) => {
        numbs[0] = Number.parseInt(input);
        events.emit("input1");
    });
});

events.on("input1", () => {
    rl.question(`Input #2: `, (input) => {
        numbs[1] = Number.parseInt(input);
        events.emit("restOfProgram");
    });
});

events.on("restOfProgram", () => {
    console.log(numbs);
    /*
        rest of program to run
    */
});

events.emit("input0");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM