在Node.js中写入循环中的文件时可能存在数据不一致

时间:2018-02-11 16:57:56

标签: javascript node.js asynchronous

我有一个数组,说... 100000个对象。我使用map函数,在每次迭代中,我构建一个字符串并将内容写入CSV,如下所示:

  entriesArray.map((entry) => {
    let str = entry.id + ',' + entry.fname + ',' + entry.lname + ',' +
    entry.address + ',' + entry.age + ',' + entry.sex + '\n'
    writeToFile(str);
  });

writeToFile函数:

const writeToFile = (str) => {
  fs.appendFile(outputFileName + '.csv', str, (err) => {
    if (err) throw err;
  });
};

这可以按预期工作,但我担心如果有这么多异步写操作可能会导致任何数据不一致。所以我的问题是,这样安全吗?或者有更好的方法来做到这一点。

顺便说一下,MAC OS上的相同代码抛出错误错误:ENFILE:文件表溢出,打开' output.csv'。经过一番研究,我了解到这是因为OSX具有非常低的打开文件限制。有关详细信息,请参见here

我再次希望改进我的文件写入机制也可以解决这个问题。

1 个答案:

答案 0 :(得分:3)

你是正确的认识到这不是一种好的编码方式,因为不能保证异步写入的顺序(特别是如果写入很大并且可能需要对磁盘执行多次实际写入操作)。并且,请记住fs.appendfile()实际上包含三个异步操作fs.open()fs.write()fs.close()。而且,正如您所看到的,这会同时打开大量文件句柄,因为它会尝试并行执行每一次写入操作。这些都不是必需的。

我建议您将要编写的文本构建为字符串,并在最后执行一次写入,因为似乎没有理由单独编写每个文本。这也会更有效率:

writeToFile(entriesArray.map((entry) => {
    return entry.id + ',' + entry.fname + ',' + entry.lname + ',' +
        entry.address + ',' + entry.age + ',' + entry.sex + '\n';
}).join(""));

假设您entriesArray中有1000件物品。您的方案是为每个条目打开,写入和关闭3000个磁盘操作。我建议的代码执行3次磁盘操作。这应该明显更快并且具有保证的写入顺序。

此外,您确实需要考虑正确的错误处理。使用类似的东西:

if (err) throw err;

在异步回调中是不正确的错误处理。这引发了一个你无法处理的异步事件。这是关于计划:

const writeToFile = (str, fn) => {
  fs.appendFile(outputFileName + '.csv', str, (err) => {
    fn(err);
  });
};

writeToFile(entriesArray.map((entry) => {
    return entry.id + ',' + entry.fname + ',' + entry.lname + ',' +
        entry.address + ',' + entry.age + ',' + entry.sex + '\n';
}).join(""), function(err) {
    if (err) {
       // error here
    } else {
       // success here
    }
});