63

我了解 Node.js 中异步事件的基本原理,并且我正在学习如何以这种方式编写代码。但是,我遇到了以下情况:

我想编写偶尔暂停用户输入的代码。

该程序不打算用作服务器(尽管目前它打算用于命令行)。我意识到这是 Node.js 的一种非典型用法。我的目标是最终将程序迁移回客户端 Javascript 应用程序,但我发现在 Node.js 中工作既令人着迷,又对调试非常有用。这让我回到了说明问题的示例:

它读入一个文本文件并输出每一行,除非该行以“?”结尾。在这种情况下,它应该暂停让用户澄清该行的含义。目前我的程序首先输出所有行并等待最后的澄清。

有没有办法强制 node.js 在条件触发的情况下准确地暂停命令行输入(即,行以“​​?”结尾)?

var fs = require("fs");
var filename = "";
var i = 0;
var lines = [];

// modeled on http://st-on-it.blogspot.com/2011/05/how-to-read-user-input-with-nodejs.html
var query = function(text, callback) {
    process.stdin.resume();
    process.stdout.write("Please clarify what was meant by: " + text);
    process.stdin.once("data", function(data) {
        callback(data.toString().trim());
    });
};

if (process.argv.length > 2) {
    filename = process.argv[2];
    fs.readFile(filename, "ascii", function(err, data) {
        if (err) {
            console.error("" + err);
            process.exit(1);
        }
        lines = data.split("\n");
        for (i = 0; i < lines.length; i++) {
            if (/\?$/.test(lines[i])) { // ask user for clarification
                query(lines[i], function(response) {
                    console.log(response);
                    process.stdin.pause();
                });
            }
            else {
                console.log(lines[i]);
            }
        }
    });
}
else {
    console.error("File name must be supplied on command line.");
    process.exit(1);
}  
4

3 回答 3

117

这是另一种没有依赖关系的方式(readline是内置的)

const readline = require('readline');

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

    return new Promise(resolve => rl.question(query, ans => {
        rl.close();
        resolve(ans);
    }))
}


const ans = await askQuestion("Are you sure you want to deploy to PRODUCTION? ");
于 2018-06-16T17:59:46.133 回答
13

诀窍是不要迭代地执行它,而是递归地执行 for 循环。因此下一行是回调中的 printOut,即在打印行之后调用 A: 或在处理控制台输入之后调用 B:。

var fs = require("fs");

// modeled on http://st-on-it.blogspot.com/2011/05/how-to-read-user-input-with-nodejs.html
function query(text, callback) {
    'use strict';
    process.stdin.resume();
    process.stdout.write("Please clarify what was meant by: " + text);
    process.stdin.once("data", function (data) {
        callback(data.toString().trim());
    });
}

function printLinesWaitForQuestions(lines, someCallbackFunction) {
    'use strict';

    function continueProcessing() {
        if (lines.length) {
            printNextLine(lines.pop());
        } else {
            someCallbackFunction();
        }
    }

    function printNextLine(line) {

        if (/\?$/.test(line)) { // ask user for clarification
            query(line, function (response) {
                console.log(response);
                process.stdin.pause();
                continueProcessing();
            });
        } else {
            console.log(line);
            continueProcessing();
        }
    }

    continueProcessing();
}

if (process.argv.length > 2) {
    var filename = process.argv[2];
    fs.readFile(filename, "ascii", function (err, data) {
        'use strict';

        if (err) {
            console.error("" + err);
            process.exit(1);
        }

        var lines = data.split("\n");
        printLinesWaitForQuestions(lines, function () {
            console.log('Were done now');
        });
    });
} else {
    console.error("File name must be supplied on command line.");
    process.exit(1);
}

这是一个很好的解决方案,原因有两个:

  1. 它相对干净,整个过程可以包含在它自己的函数闭包中,可能会导致模块化。

  2. 它不会破坏您可能想要做的其他异步事情。

    没有迭代等待循环,并且每个您拥有的行数组只启动一个异步任务。如果在您的版本中,您有数百万行怎么办?你会立即启动数百万个异步输出......糟糕!

    递归方法不仅允许您想要执行的其他异步工作更好地并发,而且您不会因来自一个函数调用的小型异步任务而阻塞事件循环。这可能会导致内存问题、性能下降和其他值得避免的问题,尤其是在大输入时。
于 2013-08-12T19:15:08.177 回答
12

我发现一个模块可以很容易地完成是或否:

https://www.npmjs.com/package/cli-interact

安装:npm install cli-interact --save-dev

如何使用直接取自 npm 站点:

var query = require('cli-interact').getYesNo;
var answer = query('Is it true');
console.log('you answered:', answer);
于 2015-06-26T05:40:29.247 回答