【问题标题】:node.js readfile woesnode.js 读取文件问题
【发布时间】:2012-05-09 06:35:18
【问题描述】:

以下代码创建一个文本文件,然后读取它、覆盖它并再次读取它。除了创建文件之外,三个 I/O 操作都是使用 Node.js 异步 readFile 和 writeFile 执行的。

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

  • 开始...
  • 完成。
  • 第一次读取返回 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.");

【问题讨论】:

    标签: node.js


    【解决方案1】:

    我遇到了类似的问题。我正在将文本写入文件,并且有一个更改处理程序告诉我文件何时更改,此时我尝试异步读取它以进一步处理文件的新内容。

    大部分时间都有效,但在某些情况下,异步读取的回调返回一个空字符串。因此,也许更改事件发生在文件完全写入之前,所以当我尝试读取它时,我得到了空字符串。现在人们可能希望 ASYNC 读取能够识别出文件正在写入过程中,因此应该等到写入操作完成。似乎在 Node.js 中写入不会锁定文件被读取,因此如果您在写入过程中尝试读取,则会得到意想不到的结果。

    通过检测 ASYNC 读取的结果是否为空字符串,我能够解决此问题,如果是,则对同一文件进行额外的 SYNC 读取。这似乎产生了正确的内容。是的 SYNC 读取速度较慢,但​​我只在 ASYNC 读取似乎未能产生预期内容时才这样做。

    【讨论】:

      【解决方案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 libraryseries函数:

      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 的对象)。

      【讨论】:

      • 但我认为我无法在 node.js 中获得竞争条件 (???) 由于 JavaScript 是单线程的,如何在读取过程中执行写入操作? readFile 和 writeFile 不是原子的吗?
      • Javascript 是单线程在 Javascript 事件循环中。任何跨越事件循环的东西都是要求 另一个线程中的本机代码做某事,而你不保证你会有同步行为.您也不能保证您的回调将按照它们指定的顺序被调用——它们仅在底层代码完成时被调用,因此回调响应顺序很可能是 2、1、3。 (不过,我认为这里不是这种情况,我们在这里处理的是 I/O 操作的副作用。)
      • @HectorCorrea 如果您认为无法在为事件 IO 设计的框架中获得竞争条件,那么您就大错特错了。 node.js 在单个线程上运行,但将写入交给操作系统并接收事件以继续处理。这意味着在您的第一个readFile 被交给操作系统之后,writeFile 会执行,readFile 稍后会收到data 事件(在不确定的时间之后从操作系统)。
      • @DavidEllis 感谢您指出该异步库。它看起来非常棒。
      • @HectorCorrea,也许你使用了fs.openfs.fsync 来强制完全写入状态?
      猜你喜欢
      • 2017-12-23
      • 1970-01-01
      • 2017-11-30
      • 2014-07-14
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多