阻止Node.js中的stdin

时间:2014-04-13 15:15:56

标签: node.js stdin blocking

简短说明:

我试图在Node.js中编写一个需要等待用户输入的简单游戏。我如何在转弯循环内部avoid callback地狱(例如凌乱的代码),每个转弯循环迭代需要阻止并等待来自标准输入的输入?

长解释:

当有人要求阻止stdin输入时,我在StackOverflow上读到的所有解释似乎都是"那不是Node.js的意思!"

我知道Node.js设计为非阻塞,我也理解为什么。但是我觉得它让我陷入困境和如何解决这个问题的困难之中。我觉得我有三个选择:

  1. 找到一种方法来阻止stdin并保留我的while循环
  2. 抛弃while循环,而不是在前一轮结束时递归调用方法(如nextTurn)。
  3. 抛弃while循环,改为使用setTimeout(0,...)或类似的东西,每当转弯结束时调用一个方法(如nextTurn)。
  4. 使用选项(1)我将反对Node.js非阻塞IO的原则。 使用选项(2),我将最终达到堆栈溢出,因为每次调用都会向调用堆栈添加另一个转弯。 使用选项(3),我的代码最终会变得一团糟。

    Node.js内部有默认功能,标记为** Sync(例如,参见fs库或睡眠功能),我想知道为什么没有用于获取用户输入的Sync方法?如果我要写一些与fs.readSync类似的东西,我将如何去做并仍然遵循最佳实践?

3 个答案:

答案 0 :(得分:11)

刚刚发现: https://www.npmjs.com/package/readline-sync

示例代码(执行npm install readline-sync后)

var readlineSync = require('readline-sync');
while(true) {
  var yn = readlineSync.question("Do you like having tools that let you code how you want, rather than how their authors wanted?");
  if(yn === 'y') {
    console.log("Hooray!");
  } else {
    console.log("Back to callback world, I guess...");
    process.exit();
  }
}

到目前为止,唯一的问题是"这不是节点的意图如何使用!"合唱,但我有耳塞:)

答案 1 :(得分:2)

我同意关于转向基于事件的系统的评论,并且会放弃循环。我将基于文本的处理的快速示例放在一起,可以用于简单的文本游戏。

var fs = require('fs'),
    es = require('event-stream');

process.stdin
    .pipe(es.split())
    .on('data', parseCommand);

var actionHandlers = {};

function parseCommand(command) {
    var words = command.split(' '),
        action = '';

    if(words.length > 1) {
        action = words.shift();
    }

    if(actionHandlers[action]) {
        actionHandlers[action](words);
    } else {
        invalidAction(action);
    }
}

function invalidAction(action) {
    console.log('Unknown Action:', action);
}

actionHandlers['move'] = function(words) {
    console.log('You move', words);
}

actionHandlers['attack'] = function(words) {
    console.log('You attack', words);
}

现在,您可以将操作分解为离散函数,您可以使用中心actionHandlers变量进行注册。这使得添加新命令几乎是微不足道的。如果您可以添加一些有关上述方法为什么不适合您的详细信息,请告诉我,我会修改答案。

答案 2 :(得分:0)

至少在我的用例中,ArtHare的解决方案阻止了后台执行,包括那些由诺言启动的后台执行。尽管这段代码不太好用,但它确实阻止了当前函数的执行,直到完成从stdin的读取为止。

尽管此代码必须从异步函数内部运行,但是请记住,从顶级上下文(直接从脚本而不是任何其他函数中包含)运行异步函数将阻止该功能,直到完成为止。

下面是演示用法的完整.js脚本,并已通过节点v8.12.0进行了测试:

const readline = require('readline');
const sleep = (waitTimeInMs) => new Promise(resolve => setTimeout(resolve, waitTimeInMs));

async function blockReadLine() {

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

    let result = undefined;
    rl.on('line', function(line){
        result = line;
    })

    while(!result) await sleep(100);

    return result;
}

async function run() {
    new Promise(async () => {
        while(true) {
            console.log("Won't be silenced! Won't be censored!");
            await sleep(1000);
        }
    });

    let result = await blockReadLine();
    console.log("The result was:" + result);
    process.exit(0);
}

run();