【问题标题】:Waiting for user to enter input in Node.js等待用户在 Node.js 中输入输入
【发布时间】:2013-08-14 04:12:48
【问题描述】:

我了解 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);
}  

【问题讨论】:

    标签: node.js


    【解决方案1】:

    诀窍是不要迭代地执行它,而是递归地执行 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. 它不会破坏您可能想做的其他异步操作。 没有迭代等待循环,并且每个您拥有的行数组只启动一个异步任务。如果在您的版本中,您有数百万行怎么办?您会立即启动数百万个异步输出……糟糕! 递归方法不仅允许您想要执行的其他异步工作更好地并发,而且您不会因来自一个函数调用的小型异步任务而阻塞事件循环。这可能会导致内存问题、性能下降和其他值得避免的问题,尤其是在大输入时。

    【讨论】:

    • 哇,这很有帮助!它不仅解决了这个问题,我还可以看到它是如何概括的。非常感谢。
    【解决方案2】:

    我找到了一个模块,可以很容易地判断是或否:

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

    【讨论】:

    • 我很欣赏这个建议,它看起来很有希望;但是当我运行它时崩溃。但是,它指的是 [link]github.com/anseki/readline-sync,它看起来也很有希望。
    【解决方案3】:

    这是另一种没有依赖关系的方式(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? ");
    

    【讨论】:

    • 这太完美了,简洁可爱!谢谢!
    • 我遇到ReferenceError: require is not defined in ES module scope, you can use import instead 错误
    • @alper 你使用 mjs 或 babel 什么的? require 应该在普通的旧节点(不是浏览器!)中工作。不过不管怎样,改成import * as readline from 'readline'
    猜你喜欢
    • 1970-01-01
    • 2012-05-21
    • 2014-01-26
    • 2016-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-23
    • 2013-05-25
    相关资源
    最近更新 更多