如何在 nodejs 中使用异步获取同步 readline 或 "simulate" ?

How to get synchronous readline, or "simulate" it using async, in nodejs?

我想知道是否有一种简单的方法来获取 "synchronous" readline 或者至少在 node.js

中获取同步 I/O 的外观

我用过类似的东西但是很别扭

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

var i = 0;
var s1 = '';
var s2 = '';

rl.on('line', function(line){
    if(i==0) { s1 = line; }
    else if(i==1) { s2 = line; }
    i++;
})

rl.on('close', function() {
    //do something with lines
})'

而不是这个,如果它像

这样简单的话,我会更喜欢
var s1 = getline(); // or "await getline()?"
var s2 = getline(); // or "await getline()?"

有用条件:

(a) 最好不要使用外部模块或 /dev/stdio 文件句柄,我正在向代码提交网站提交代码,但这些在那里不起作用

(b) 可以使用 async/await 或生成器

(c) 应该是基于行的

(d) 处理前不应要求将整个标准输入读入内存

因为我不知道你需要多少个字符串,所以我把它们都放在一个数组中

如果您需要更详细的答案或者我的回答不准确,请不要犹豫发表评论:

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

var i = 0;
var strings = [];

rl.on('line', function(line) {
    // 2 lines below are in case you want to stop the interface after 10 lines
    // if (i == 9)
    //  rl.close()
    strings[i] = line
    i++
}).on('close', function() {
    console.log(strings)
})
// this is in case you want to stop the program when you type ctrl + C
process.on('SIGINT', function() {
    rl.close()
})

这是一个示例,但它需要在给出结果之前读取整个标准输入,但这并不理想

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


function lineiterator() {
    var currLine = 0;
    var lines = [];
    return new Promise(function(resolve, reject) {

        rl.on('line', function (line){
            lines.push(line)
        })
        rl.on('close', function () {
            resolve({
                next: function() {
                    return currLine < lines.length ? lines[currLine++]: null;
                }
            });
        })
    })
}

例子

lineiterator().then(function(x) {
    console.log(x.next())
    console.log(x.next())
})

$ echo test$\ntest | node test.js
test
test

使用生成器,您的示例将如下所示:

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

var i = 0;
var s1 = '';
var s2 = '';

var iter=(function* () {
    s1 = yield;
    i++;
    s2 = yield;
    i++;
    while (true) {
        yield;
        i++;
    }
})(); iter.next();
rl.on('line', line=>iter.next(line))

rl.on('close', function() {
    //do something with lines
})

所以 yield 在这里就像一个阻塞 getline() 并且您可以按通常的顺序方式处理行。


UPD:
async/await 版本可能如下所示:

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

var i = 0;
var s1 = '';
var s2 = '';

var continuation;
var getline = (() => {
    var thenable = {
        then: resolve => {
            continuation = resolve;
        }
    };
    return ()=>thenable;
})();
(async function() {
    s1 = await getline();
    i++;
    s2 = await getline();
    i++;
    while (true) {
        await getline();
        i++;
    }
})();
rl.on('line', line=>continuation(line))

rl.on('close', function() {
    //do something with lines
})

在这两个"synchronous"版本中,i不用于区分行,仅用于统计行数。

试试这个。它仍然不是同步行读取功能的完美复制——例如async 函数仍然在稍后发生,因此您的一些调用代码可能会乱序执行,并且您不能从正常的 for 循环中调用它——但它比典型的 .on.question 代码。

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

// new function that promises to ask a question and 
// resolve to its answer
function ask(questionText) {
  return new Promise((resolve, reject) => {
    readlineInterface.question(questionText, (input) => resolve(input) );
  });
}

// launch your program since `await` only works inside `async` functions
start()

// use promise-based `ask` function to ask several questions
// in a row and assign each answer to a variable
async function start() {
  console.log()
  let name = await ask("what is your name? ")
  let quest = await ask("what is your quest? ")
  let color = await ask("what is your favorite color? ")
  console.log("Hello " + name + "! " + 
    "Good luck with " + quest + 
    "and here is a " + color + " flower for you.");
  process.exit() 
}

更新:https://www.npmjs.com/package/readline-promise implements it (source code here: https://github.com/bhoriuchi/readline-promise/blob/master/src/index.js#L192 ). It implements several other features as well, but they seem useful too, and not too overengineered, unlike some other NPM packages that purport to do the same thing. Unfortunately, I can't get it to work due to https://github.com/bhoriuchi/readline-promise/issues/5但我喜欢它对中心功能的实现:

function ask(questionText) {
  return new Promise((resolve, reject) => {
    readlineInterface.question(questionText, resolve);
  });
}

readline模块一样,还有一个模块叫做readline-sync,它采用同步输入。

示例:

const reader = require("readline-sync"); //npm install readline-sync
let username = reader.question("Username: ");
const password = reader.question("Password: ",{ hideEchoBack: true });
if (username == "admin" && password == "foobar") {
    console.log("Welcome!")
}

以防将来有人偶然发现这里

Node11.7 使用 async await

添加了对此 doc_link 的支持
const readline = require('readline');
//const fileStream = fs.createReadStream('input.txt');

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

for await (const line of rl) {
  console.log(line)
}

记得用async function(){}包起来,否则会得到reserved_keyword_error

const start = async () =>{
    for await (const line of rl) {
        console.log(line)
    }
}
start()

要读取单独的一行,您可以手动使用 async 迭代器

const it = rl[Symbol.asyncIterator]();
const line1 = await it.next();

我想这就是你想要的:

const readline = require('readline');

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

const getLine = (function () {
    const getLineGen = (async function* () {
        for await (const line of rl) {
            yield line;
        }
    })();
    return async () => ((await getLineGen.next()).value);
})();

const main = async () => {
    let a = Number(await getLine());
    let b = Number(await getLine());
    console.log(a+b);
    process.exit(0);
};

main();

注意:此答案使用实验性功能并且需要 Node v11.7

你可以把它包装在一个承诺中 -

const answer = await new Promise(resolve => {
  rl.question("What is your name? ", resolve)
})
console.log(answer)

文档中提供了最简单(也是首选)的选项。 https://nodejs.org/api/readline.html#rlquestionquery-options-callback

const util = require('util');
const question = util.promisify(rl.question).bind(rl);

async function questionExample() {
  try {
    const answer = await question('What is you favorite food? ');
    console.log(`Oh, so your favorite food is ${answer}`);
  } catch (err) {
    console.error('Question rejected', err);
  }
}
questionExample();

我们可以结合使用 promise 和 process.stdin 事件来模拟同步输入系统

const { EOL } = require("os");
const getLine = async () =>
    (
        await new Promise((resolve) => {
            process.stdin.on("data", (line) => {
                resolve("" + line);
            });
        })
    ).split(EOL)[0];

const line = await getLine();
console.log(line);