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

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

これは、次の 2 つの理由から優れたソリューションです。

  1. これは比較的クリーンで、プロセス全体を独自の関数クロージャに含めることができるため、モジュール化につながる可能性があります。

  2. あなたがやりたいかもしれない他の非同期的なことを壊すことはありません。

    反復待機ループはなく、行の配列ごとに起動される非同期タスクは 1 つだけです。あなたのバージョンに何百万もの行があるとしたら? 何百万もの非同期出力を瞬時にスピンアップしたでしょう...悪い!

    再帰的な方法は、実行したい他の非同期作業の同時実行性を向上させるだけでなく、1 つの関数呼び出しからの小さな非同期タスクでイベント ループを詰まらせることもありません。これにより、メモリの問題、パフォーマンスの低下、その他の避けるべき問題が発生する可能性があります (特に入力が大きい場合)。
于 2013-08-12T19:15:08.177 に答える