node.js读取文件的问题
下面的代码创建一个文本文件,然后读取它,覆盖它,然后再次读取它。除了创建文件外,三个I/O操作都是使用Node.js async readFile和writeFile执行的 我不明白为什么第一次读取返回无错误,但也没有数据。此代码的输出为:node.js读取文件的问题,node.js,Node.js,下面的代码创建一个文本文件,然后读取它,覆盖它,然后再次读取它。除了创建文件外,三个I/O操作都是使用Node.js async readFile和writeFile执行的 我不明白为什么第一次读取返回无错误,但也没有数据。此代码的输出为: 开始 完成了 第一次读取返回空数据 写完了好吗 第二次读取返回的数据:更新的文本 即使操作以任意顺序发生(由于其异步性质),我也不会期望得到一个“空数据”对象 你知道为什么我在读取文件时会得到一个空数据吗(而且没有错误) 我可以做些什么来确保文件内容被读
- 开始
- 完成了
- 第一次读取返回空数据强>
- 写完了好吗
- 第二次读取返回的数据:更新的文本
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.");
您的代码正在询问比赛条件。您的第一次同步写入可能正在写入文件,但随后您的第一次读取、第二次写入和第二次读取将同时放入事件循环中
这里会发生什么?第一次读取从文件系统获得读取权限,第二次写入从文件系统获得写入权限,并立即将文件归零以备将来更新,然后第一次读取读取现在为空的文件。然后第二次写入开始写入数据,第二次读取直到完成后才获得读取权限
如果要避免这种情况,需要使用以下流:
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);
});
});
});
如果“金字塔”侮辱了您的编程敏感性(为什么不呢?),请使用sseries
函数:
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中,写入不会锁定文件的读取,因此如果在写入过程中尝试读取,会得到意外的结果
通过检测异步读取的结果是否为空字符串,以及如果是空字符串,则对同一文件执行额外的同步读取,我能够绕过这个问题。这似乎产生了正确的内容。是的,同步读取速度较慢,但是我只在异步读产生期望的内容时才这样做。但是我认为我不能在NoDE.js(??)中获得竞争条件,因为JavaScript是单线程的,在读的中间怎么写执行?readFile和writeFile不是原子的吗?Javascript在Javascript事件循环中是单线程的。任何跨越事件循环的操作都会要求另一个线程中的本机代码执行某些操作,并且不能保证您将具有同步行为。您也不能保证回调将按照指定的顺序调用——它们只在底层代码完成时调用,因此回调响应顺序很可能是2、1、3。(我不认为这里是这种情况,但是,我们这里处理的是I/O操作的副作用。)@HectorCorrea如果你认为你不能在为事件IO设计的框架中获得比赛条件,那你就错了。node.js在单个线程上运行,但不向操作系统进行写操作并接收事件以继续处理。这意味着在您的第一个
readFile
被交给操作系统后,writeFile
执行并readFile
稍后接收数据
事件(在不确定的时间之后从操作系统接收)。@DavidEllis感谢您指出异步库。它看起来非常棒。@HectorCorrea,如果您使用fs.open
并强制状态完全写入,可能吗?