【问题标题】:Node.js fs.writeFile() doesn't seem asynchronousNode.js fs.writeFile() 似乎不是异步的
【发布时间】:2021-08-14 02:59:20
【问题描述】:

如果我们查看 fs.writeFile() method 的 Node.js 文档,我们会发现它本质上是异步的。

现在,考虑到这一点,我设置了以下代码来测试这个想法:

const fs = require('fs');

function sync(delay) {
    const startingTime = new Date();
    while ((new Date() - startingTime) < delay) {
        // keep this loop going
    }
}


fs.writeFile(__dirname + '/hello.txt', 'Hello World', function() {});

sync(4000);

我已经在 VSCode 中打开了 hello.txt 文件,每当向文件中写入一些数据时,VSCode 都会实时显示它。

我希望这段代码在 4 秒之前将 Hello World 写入文件 hello.txt(这是由 sync(4000) 引起的延迟),因为 fs.writeFile() 本质上是异步的(因为文件说)。然而,令我惊讶的是,任何数据都会在 4 秒后写入文件。

这似乎根本不是异步的!

我的问题是,为什么在整个脚本同步执行之前没有将数据写入文件hello.txt

我怀疑(顺便说一句,我对操作系统线程了解不多)当调用fs.writeFile() 时,它会在另一个空闲的操作系统线程上异步写入文件(不阻塞主线程),但这并没有似乎没有发生。

我一直在测试很多其他与文件写入实用程序相关的东西,例如可写流,并且遇到了同样的问题。除非所有同步执行完成,否则不会写入任何内容。

我需要对这种内部事物进行清楚的解释。

【问题讨论】:

  • 当您从setTimeout 函数中写入文件时会发生什么?
  • @vanowm 结果相同。
  • 如果你从setTimeout执行sync
  • @vanowm 我不明白你的意思。
  • setTimeout(sync(4000));

标签: javascript node.js buffer file-writing


【解决方案1】:

试试这个:

import { writeFile } from 'fs/promises'

function sync(delay) {
  const startingTime = new Date();
  while ((new Date() - startingTime) < delay) {
      // keep this loop going
  }
}

(async () => {
  await writeFile(__dirname + '/hello.txt', 'Hello World');

  sync(4000)
})();

【讨论】:

  • 感谢您的回答,但我想了解导致上述代码在 4 秒后写入的原因,而不是如何解决。
【解决方案2】:

虽然writeFile 在行为上看起来可能是异步的,但 NodeJS 受到 JavaScript 是单线程语言这一事实​​的限制。为了模拟异步,NodeJS 使用event loop 来处理动作。

我不确定writeFile 函数的内部工作原理,但从代码的运行方式来看,可以假设事件循环中添加了 3 个或更多任务(一个用于创建文件,一种用于写入创建的文件,另一种用于处理回调)。多个事件对于理解代码执行方式的原因很重要。

writeFile 函数调用执行时,“hello.txt”文件生成为空(创建文件,第一个任务)。但是,自从执行了writeFile 调用以来,JavaScript 引擎已经继续执行并执行了问题的罪魁祸首,即您的sync 函数。 sync 函数利用 while 循环来模拟延迟,而不是通常使用的 setTimeout 函数,这是了解内部情况的关键。

在这种情况下,while 循环所做的是在写入创建的文件之前劫持事件循环(这解释了为什么创建文件但未写入文件),因为 while 是一个阻塞操作。您可以通过在 sync 函数之前使用 writeFileSync 添加另一个 I/O 操作来验证此行为。这两个文件都将被创建,但只有 writeFileSync 函数的文件会在其中写入任何内容。

由于while 循环阻塞了事件循环,数据会在延迟之后写入文件,因为那是while 循环完成执行并释放事件循环以执行最终任务的时间writeFile 函数。

注意:这个答案是在一定程度上不确定的,因为我还没有深入探讨 NodeJS 事件循环如何工作的内部结构。我在回答这个问题时参考的文件在这里:

【讨论】:

  • 我还是不清楚。您所说的是事件循环被阻止,因此写入操作(即write() 方法)不会执行。但是我认为当write()被调用时,它可以在一个空闲线程上创建一个文件写入过程(在后台异步),然后可以进行写入(这是在后台发生的,而不是同步发生的)——为什么会这样程序在写入之前必须等待全部 4 秒。
  • @coderboy 在这种情况下如何实现对文件的写入并不重要,因为您的while 首先阻止了写入。将问题从“它为什么等待”转移到“为什么它可以等待”是一个我无法回答的设计哲学问题,但请记住,NodeJS 不像其他语言那样的逻辑可以工作,因为 NodeJS 是基于 event-driven programming 的哲学。
  • 运行 Node 的主线程被 sync() 调用阻塞,这是分配给运行 JavaScript 代码的唯一线程。内部写入的文件不应该在这里发生。这就是我要强调的。在内部执行 JavaScript 异步内容时,通常还有其他线程可用,所有异步魔法都会发生。我想知道为什么文件写入没有发生在这个单独的线程中,不受主线程同步延迟的影响。
  • 我怀疑(但我再次不确定,因为我不懂 C++,并且从头开始深入研究 Node.js 的内部结构需要很多时间)文件写入被阻止,因此程序可以在一个地方(通常是缓冲区)从 all 调用程序中的write()收集数据,然后将所有数据写入一次,而不是一个一个地写入每个数据。我只是想让一些 Node 极客确认或纠正我的问题......
  • @coderboy 不幸的是,我不确定写入延迟的原因,但sync() 肯定会阻止文件写入本身。写入本身是异步的(参见:Worker Pool)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-21
  • 1970-01-01
  • 2015-05-16
  • 1970-01-01
相关资源
最近更新 更多