9

次のコードは、テキストファイルを作成し、それを読み取り、上書きして、再度読み取ります。ファイルの作成を除いて、3つのI/O操作はNode.js非同期readFileとwriteFileを使用して実行されます。

最初の読み取りでエラーが返されないのにデータも返されない理由がわかりません。このコードの出力は次のとおりです。

  • 起動...
  • 終わり。
  • 最初に返された空のデータを読み取ります!
  • 書き込み終了OK
  • 2番目に返されたデータの読み取り:更新されたテキスト

操作が任意の順序で発生したとしても(非同期の性質のため)、「空のデータ」オブジェクトを取得することは期待していませんでした。

ファイルを読み取るときに空のデータを取得する(エラーが発生しない)理由はありますか?

ファイルの内容が確実に読み取られるようにするためにできることはありますか?

var fs = require('fs');
var fileName = __dirname + '/test.txt';

// Create the test file (this is sync on purpose)
fs.writeFileSync(fileName, 'initial test text', 'utf8');


console.log("Starting...");

// Read async
fs.readFile(fileName, 'utf8', function(err, data) {
    var msg = "";
    if(err)
        console.log("first read returned error: ", err);
    else {
        if (data === null) 
            console.log("first read returned NULL data!");
        else if (data === "") 
            console.log("first read returned EMPTY data!");
        else
            console.log("first read returned data: ", data);
    }
});


// Write async
fs.writeFile(fileName, 'updated text', 'utf8', function(err) {
    var msg = "";
    if(err)
        console.log("write finished with error: ", err);
    else
        console.log("write finished OK");
});


// Read async
fs.readFile(fileName, 'utf8', function(err, data) {
    var msg = "";
    if(err)
        console.log("second read returned error: ", err);
    else
        if (data === null) 
            console.log("second read returned NULL data!");
        else if (data === "") 
            console.log("second read returned EMPTY data!");
        else
            console.log("second read returned data: ", data);
});


console.log("Done.");
4

2 に答える 2

18

あなたのコードは競合状態を求めています。最初の同期書き込みはおそらくファイルの書き込みですが、最初の読み取り、2 番目の書き込み、2 番目の読み取りが同時にイベント ループに置かれます。

ここで何が起こったのでしょうか?最初の読み取りはファイルシステムから読み取り許可を取得し、2 番目の書き込みはファイルシステムから書き込み許可を取得し、将来の更新のためにファイルをすぐにゼロにします。次に、最初の読み取りで空のファイルを読み取ります。次に、2 番目の書き込みがデータの書き込みを開始し、2 番目の読み取りは完了するまで読み取り許可を取得しません。

これを回避したい場合は、次のフローを使用する必要があります。

fs.writeFileSync(filename, 'initial', 'utf8');
fs.readFile(filename, 'utf8', function(err, data) {
    console.log(data);
    fs.writeFile(filename, 'text', 'utf8', function(err) {
        fs.readFile(filename, 'utf8', function(err, data) {
            console.log(data);
        });
    });
});

その「ピラミッド」がプログラミングの感性を侮辱する場合 (なぜそうしないのですか?)、async ライブラリseries関数を使用します。

fs.writeFileSync(filename, 'initial', 'utf8');
async.series([
    function(callback) {
        fs.readFile(filename, 'utf8', callback);
    },
    function(callback) {
        fs.writeFile(filename, 'text', 'utf8', callback);
    },
    function(callback) {
        fs.readFile(filename, 'utf8', callback);
    }
], function(err, results) {
    if(err) console.log(err);
    console.log(results); // Should be: ['initial', null, 'text']
});

async編集:よりコンパクトですが、ライブラリや最新のJavascript機能に慣れていない人にとってはより「魔法のよう」です:

fs.writeFileSync(filename, 'initial', 'utf8');
async.series([
    fs.readFile.bind(this, filename, 'utf8'),
    fs.writeFile.bind(this, filename, 'text', 'utf8'),
    fs.readFile.bind(this, filename, 'utf8'),
], function(err, results) {
    if(err) console.log(err);
    console.log(results); // Should be: ['initial', null, 'text']
});

EDIT2: の定義を検索せずにその編集を行うために、私は正しく機能しますbind。最初のパラメーターは、thisオブジェクト (または として使用したいものthis) である必要があります。

于 2012-04-29T01:05:38.253 に答える
1

同様の問題がありました。私はファイルにテキストを書き込んでいて、ファイルがいつ変更されたかを知らせる変更ハンドラがあり、その時点でファイルの新しいコンテンツをさらに処理するために非同期で読み取ろうとしました。

ほとんどの場合は機能しましたが、ASYNC 読み取りのコールバックが空の文字列を返す場合がありました。そのため、ファイルが完全に書き込まれる前に変更イベントが発生した可能性があるため、ファイルを読み取ろうとすると空の文字列が得られました。ここで、ファイルが書き込み中であることを ASYNC 読み取りが認識し、書き込み操作が完了するまで待機することを期待できます。Node.js では、書き込みはファイルの読み取りをロックしないように思われるため、書き込み中に読み取りを試みると、予期しない結果が得られます。

ASYNC 読み取りの結果が空の文字列であるかどうかを検出し、その場合は同じファイルに対して追加の SYNC 読み取りを行うことで、この問題を回避することができました。それは正しいコンテンツを生成するようです。はい、同期読み取りは遅くなりますが、非同期読み取りが期待されるコンテンツの生成に失敗したと思われる場合にのみ行います。

于 2020-04-17T19:53:23.880 に答える