12

As the title states, I was wondering if it is possible to use promisify (https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original) for readline in node.js? I was only able to do it like this:

let data = [];
const parse = () => {
    return new Promise((resolve, reject) => {

        const rl = readline.createInterface({
            input: fs.createReadStream(path)
        });

        rl.on('line', (line) => {
            data.push(line);
        });

        rl.on('close', () => {
            resolve(data);
        });
    });
};
uglycode
  • 3,022
  • 6
  • 29
  • 55
  • You can only promisify error first callbacks, and with `promisify.custom` other types of callbacks that does not follow the pattern. For streams and event emitters you need to implement your own logic, it does share a common interface (with on `close`, on `finish` ) but the usecases are very different. – Alexandru Olaru Oct 24 '17 at 10:35
  • Yeah, that's what I suspected. Is my implementation adequate? – uglycode Oct 24 '17 at 10:48
  • 2
    The problem with your implementation is that you added the `data` in a higher scope than your promise, it will accumulate data for each `parse` use, if you use `parse` promise 2 times, the second use will have the first values appended and then the second values also. A better approach is to set the `let data` in the promise in this way for each use you will store only the new data. – Alexandru Olaru Oct 24 '17 at 10:54
  • you are absolutely right, I completely forgot that! – uglycode Oct 24 '17 at 10:57
  • 1
    Have a look at https://stackoverflow.com/questions/43638105/how-to-get-synchronous-readline-or-simulate-it-using-async-in-nodejs – Bergi Jun 05 '21 at 19:52
  • https://nodejs.org/api/readline.html#promises-api – Bergi Mar 21 '23 at 20:53

4 Answers4

15

Here you have a simple way to do it that doesn't use promisify:

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

function question(query) {
    return new Promise(resolve => {
        readline.question(query, resolve);
    });
}

async function main() {
    const name = await question('Whats your name? ');
    console.log(`hello ${name}!`);
    readline.close();
}

main();
Jesús López
  • 8,338
  • 7
  • 40
  • 66
  • 1
    starting in node 17, you can simply do `readline = require('readline').promises.createInterface(...)` and then just invoke `await readline.question('...')` directly, see https://nodejs.org/api/readline.html#class-readlinepromisesinterface – masterxilo Jun 07 '22 at 20:07
7

Here is an example of how I promisify readline.question:

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

// Prepare readline.question for promisification
readline.question[promisify.custom] = (question) => {
  return new Promise((resolve) => {
    readline.question(question, resolve);
  });
};

// Usage example:
(async () => {
  const answer = await promisify(readline.question)('What is your name? ');
  readline.close();
  console.log('Hi %s!', answer);
})();

Node (v8) documentation for custom promisified functions: https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_custom_promisified_functions

romellem
  • 5,792
  • 1
  • 32
  • 64
Josef Engelfrost
  • 2,955
  • 1
  • 29
  • 38
0

try to use bluebird which create http://bluebirdjs.com/docs/api/promise.promisifyall.html

but if the code works. then I think you don't need to promisify that since you already return it as promise.

0

Starting in Node 17, it's already promisified:

import { stdin, stdout } from 'node:process';
import * as readline from 'node:readline/promises';

const rl = readline.createInterface({ input: stdin, output: stdout });
const answer = await rl.question('What is your name? ');
console.log('Thank you', answer);
rl.close();

See the documentation:

fregante
  • 29,050
  • 14
  • 119
  • 159