3

フロー制御の目的で一連の関数を実装しようとしていますが、予期しない結果が得られます。ファイルの存在を確認する必要があります。その結果、ノードによって開始されたシェルcmdが、たとえば5000ミリ秒の間に発生します。Mixuのノードブックを使用して、順次実行にシリーズ関数を使用しています(セクション7.2.1)。ノードブックによると、ある結果を別の結果にフィードする必要がある場合、このパターンは適切ですがfinal()、引数関数からのコールバックが返される前にが実行されているため、そうではないようです。

以下は、私が見ているものを再現するために必要なすべてのコードです。

期待される出力:(チェック対象のファイルが1000ミリ秒未満で作成されたと想定)

1347312844082
true
1347312845082
true
1347312846083
true
1347312847082
true
1347312848082
true
true <-シリーズ関数の最後のparam関数のconsole.log()の最後のtrue結果。

実際の出力

1347312844082
1347312845082
1347312846083
1347312847082
1347312848082false
<-シリーズ引数コールバックの前に評価されているシリーズ関数の最後のパラメーター関数。
真真
真真
真真
_
_

スリープ関数(呼び出しを含む)からのコールバックを推測していますfs.check

  • args級数関数のパラメータがすべてslice.call(arguments)によって呼び出された結果としてプッシュバックされています
  • 次に、最後の関数にシフトします。
  • そして最後にコールバックを処理します。

しかし、これはノードブックに記述されている動作に関して矛盾しているようです。

:私はfs.existsSync、脳を壊そうとして同期呼び出し()に戻らないようにしようとしています。ノード/非同期プログラミングに関しては、最終的に「取得」する可能性があります。

    var outputFile = 'C:\\Some\\Valid\\Path\\foo.txt';
    var successCheck = false;
    series([
            sleep(1000, function () {
                printTime();
                fs.exists(outputFile, function (exists) {
                    console.log(exists);
                    successCheck = exists;
                });
            }),
            /* middle three deleted for brevity */
            sleep(1000, function () {
                printTime();
                fs.exists(outputFile, function (exists) {
                    console.log(exists);
                    successCheck = exists;
                });
            }),

            sleep(1000, function () {
                printTime();
                fs.exists(outputFile, function (exists) {
                    console.log(exists);
                    successCheck = exists;
                });
            })
            ], function() {
                console.log(successCheck);
        });


    function sleep (msInterval, callback) {
        var now = new Date().getTime();
        while(new Date().getTime() < now + msInterval) {
      // do nothing
        }
        callback();
    }

    function series (callbacks, last)  {
        var results = [];
        function next() {
            var callback = callbacks.shift();
            if(callback) {
                callback(function() {
                    results.push(Array.prototype.slice.call(arguments));
                    next();
                });
            } else {
                last(results);
            }
        }

        next();
    }

    function printTime () { var now = new Date().getTime(); console.log(now); }
4

1 に答える 1

2

あなたのコードにはいくつかの問題があります。何よりもまず、「睡眠」へのアプローチが完全に壊れています。

JavaScript (および Node) は、シングルスレッドのイベント駆動型言語です。ブロックするようなことをすると、プログラム全体がブロックされます。

関数は、ミリ秒が経過sleepするまでスピンすることにより、プログラム全体をブロックします。msInterval

これがまさに起こっていることです:

  • まず、いくつかの変数を設定します。
  • への呼び出しがありますseries。引数を渡すには、それらの引数を評価する必要があります。
  • sleep次の行で呼び出されます。
  • sleep1000ミリ秒スピンします。
  • sleepその を呼び出しますcallback
  • コールバックはコンソールに内容を出力し、非同期 I/O 要求を発行して戻ります。
  • sleepundefinedを返します。
  • への次の呼び出しsleepが呼び出されます。最後の 5 つの手順が繰り返されます。
  • ...5 秒後に、すべての呼び出しsleepが完了します。これで、次のような配列ができました。

    [undefined, undefined, undefined, undefined, undefined]
    

    sleep何も返さないので。

  • 未定義の配列と関数seriesを引数として呼び出すことができるようになりました。
  • resultsインスタンス化されます。
  • nextが呼び出されます。
  • 配列の最初の未定義はシフトオフされます。
  • undefinedは偽なので、分岐にジャンプしelseます。
  • last空の配列で呼び出します。(私たちは何も入れませんでしたresults)
  • lastコールバックは を出力し、successCheckこれはfalseであり、戻ります。
  • この時点で、同期コードは完了しています。
  • JavaScript は、イベント ループの次の反復を開始します。以前に発行した I/O 要求はおそらくこの時点で完了しているため、fs.existsコールバックは完了した順序で呼び出されます (これはおそらく、発行された順序ではありません)。
  • コールバックはコンソールに出力され、設定されますsuccessCheck(実際には再度読み取られることはありません)。
  • プログラムは終了します。

 

修正方法

whileループで回転することは決してありません。sleep次のように書き換えることができます。

function sleep(ms, cb) {
    return function(done) {
        setTimeout(function() {
            cb(done);
        }, ms);
    }
}

ここで、後で呼び出される関数を返します。このようにして、渡される配列seriesは内部sleep関数で埋められます。これを呼び出したときsleep実際にはまだ何も起きていません

series最初の配列要素を呼び出すと、実際には返された無名関数が呼び出されます。ここで、最初に呼び出しに渡された と を閉じていmsます。この関数はミリ秒のタイムアウトを設定します。それが期限切れになると、最も内側の無名関数が呼び出され、. 関数を引数として渡しますが、これは実際には'にプッシュして呼び出す無名関数です。cbsleepmscbdoneseriesnextresultsnext

シリーズの次のステップは、前のステップが完了したことを示すまで開始されないためdone、ステップの関数からコールバックを呼び出す必要があります。

sleep(1000, function (done) {
    printTime();
    fs.exists(outputFile, function (exists) {
        console.log(exists);
        successCheck = exists;
        done();
    });
});

アプローチを変える

もちろん、あなたが本当に問題を解決したい方法 (「このファイルが作成された後に何かを行うにはどうすればよいですか?」) は、ポーリングによるものではありませんfs.exists

代わりに、ファイルシステムの変更通知を使用してください! fs.watchファイルまたはフォルダーへの変更をリッスンできます。

var watcher = fs.watch('C:\\Some\\Valid\\Path\\', function(e, filename) {
    if (filename == 'foo.txt') {
        // success!
        watcher.close(); // stop watching for changes
    }
});
于 2012-09-10T23:55:50.860 に答える