2

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)

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you! This is perfect, I realize that it doesn't terminate the readline stream or check if the user input is valid. I planned on adding those once it functionally worked. In the meantime while this question was up, I looked into events and with those I was able to achieve the same result. I'll put that into an answer below in case anyone else is interested, but you definitely deserve the answer here.
@CorruptComputer - I just added another version that is simpler.
Thanks again! I pasted the solution I came to below, however I definitely think your answer is better than mine. I like the simpler solution you provided, it definitely helps with the readability of the code.
1

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");

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.