node.js readfile问题

Hec*_*rea 7 node.js

下面的代码创建一个文本文件,然后读取它,覆盖它,然后再次读取它.除了创建文件之外,还使用Node.js async readFile和writeFile执行三个I/O操作.

我不明白为什么第一次读取没有返回错误没有数据.此代码的输出是:

  • 开始...
  • 完成.
  • 先读取返回的EMPTY数据!
  • 写完了OK
  • 第二次读取返回数据:更新文本

即使操作是以任意顺序发生的(由于它们的异步性质),我也不希望得到一个"空数据"对象.

任何想法为什么我在读取文件时得到一个空数据(没有错误)?

我有什么办法可以确保读取文件内容吗?

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.");
Run Code Online (Sandbox Code Playgroud)

小智 15

您的代码要求竞争条件.您的第一次同步写入可能是写入文件,但随后您的第一次读取,第二次写入和第二次读取将同时放入事件循环.

这可能发生在什么地方?第一次读取从文件系统获得读取权限,第二次读取从文件系统获得写入权限并立即将文件归零以备将来更新,然后第一次读取将读取现在为空的文件.然后第二次写入开始写入数据,第二次读取在完成之前不会获得读取权限.

如果要避免这种情况,则需要使用以下流程:

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);
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

如果那个"金字塔"侮辱你的编程敏感性(为什么不呢?)使用异步库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']
});
Run Code Online (Sandbox Code Playgroud)

编辑:对于不熟悉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']
});
Run Code Online (Sandbox Code Playgroud)

EDIT2:在没有查找定义的情况下为我提供编辑的权利bind.第一个参数需要是this对象(或者您想要使用的任何对象this).

  • Javascript是Javascript Event Loop*中的单线程*.任何跨越事件循环的东西都是*要求*另一个线程*中的*本机代码做某事,而你没有*保证*你将有同步行为.您也无法保证将按照指定的顺序调用回调 - 只有在底层代码完成时才会调用它们,因此回调响应顺序很可能是2,1,3.(我不认为这是这种情况,但是,我们正在处理I/O操作的副作用.) (2认同)
  • @HectorCorrea如果你认为你不能在为事件IO设计的框架中获得竞争条件,那么你很遗憾.node.js在单个线程上运行,但放弃写入操作系统并接收事件以继续处理.这意味着在你的第一个`readFile`交给操作系统后,`writeFile`执行,`readFile`后来接收`data`事件(后来在不确定的时间从操作系统). (2认同)