Ожидание ввода пользователем ввода Node.js

Я понимаю обоснование в Node.js для асинхронных событий, и я изучаю, как писать код таким образом. Однако я придерживаюсь следующей ситуации:

Я хочу написать код, который иногда приостанавливается для ввода пользователем.

Программа не предназначена как сервер (хотя в настоящее время она предназначена для командной строки). Я понимаю, что это нетипичное использование Node. Моя цель - в конечном итоге перенести программу на клиентское 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);
}  

Ответы

Ответ 1

Здесь другой способ, который не имеет зависимостей (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? ");

Ответ 2

Хитрость заключается в том, чтобы не делать это итеративно, но рекурсивно для цикла for. Так что следующая строка будет printOut в обратном вызове, которая называется либо: после печати строки, либо 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);
}

Это хорошее решение по двум причинам:

  • Он относительно чист, и весь процесс может содержаться в его закрытии собственной функции, что потенциально приводит к модуляции.
  • Он не нарушает другие асинхронные вещи, которые вы, возможно, захотите сделать. Нет итеративного ожидания цикла и запускается только одна асинхронная задача для каждого массива строк. Что, если в вашей версии у вас были миллионы строк? Вы бы мгновенно создали миллионы асинхронных выходов... BAD! Рекурсивный метод не только позволяет лучше concurrency выполнять другую работу async, но вы не забиваете цикл событий с помощью мини-асинхронных задач из одного вызова функции. Это может вызвать проблемы с памятью, ухудшение производительности и другие проблемы, которые можно избежать, особенно на больших входах.

Ответ 3

Я нашел модуль, который делает это очень легко для да или нет:

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