You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js异步执行流疑问:为何调用fileHandle.write会影响文件状态?

问题:为何fileHandle.write('[1]')的存在会影响后续write操作的文件状态

我正在学习JavaScript和Node.js,仍对异步执行逻辑感到困惑。我知晓以下代码并非最佳实践,但想理解:为何是否调用fileHandle.write('[1]'),会影响fileHandle.write('[2]')执行时文件是否处于打开状态。我在Node.js文档中看到相关说明:

在未等待Promise决议(或拒绝)的情况下,多次对同一文件使用filehandle.write()是不安全的。这种场景应使用filehandle.createWriteStream()。

无错误代码

import {createServer} from 'node:http'
import {open} from 'node:fs/promises'
import {write} from 'node:fs'
import {json} from 'node:stream/consumers'

const serveur = createServer(async (request,response) => {
    const path = './tp_CRUD/storage/my_file.json'
    const fileHandle = await open(path, 'w+')
    try {
        await fileHandle.read()
        .then(() => {
            fileHandle.write('[1]') // <= 无错误,此时文件仍处于打开状态

            json(request) // 将请求内容解析为JSON的Promise
            .then(() => {
                fileHandle.write('[2]') // <= 无错误,因[1]行的存在,文件仍保持打开
            })
        })
    } catch {
        console.log('error with POST request')
    } finally {
        fileHandle.close()
    }
    response.end()
})
serveur.listen('3000')

报错代码

import {createServer} from 'node:http'
import {open} from 'node:fs/promises'
import {write} from 'node:fs'
import {json} from 'node:stream/consumers'

const serveur = createServer(async (request,response) => {
    const path = './tp_CRUD/storage/my_file.json'
    const fileHandle = await open(path, 'w+')
    try {
        await fileHandle.read()
        .then(() => {
            json(request) // 将请求内容解析为JSON的Promise
            .then(() => {
                fileHandle.write('[2]') // <= 报错,文件已关闭,尽管处于try{}块中?
            })
        })
    } catch {
        console.log('error with POST request')
    } finally {
        fileHandle.close()
    }
    response.end()
})
serveur.listen('3000')

报错栈信息

node:internal/fs/promises:436
    const err = new Error('file closed');
                ^

Error: file closed
    at fsCall (node:internal/fs/promises:436:17)
    at FileHandle.write (node:internal/fs/promises:207:12)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 'EBADF',
  syscall: 'write'
}

Node.js v21.1.0

原因分析

核心问题在于异步操作的执行顺序和Node.js内部对文件句柄的引用管理:

1. 两段代码的共同执行流程

  • 通过await open获取到文件句柄fileHandle,进入try块
  • await fileHandle.read()会等待读取操作完成,随后执行.then()回调函数
  • .then()内部的同步代码执行完毕后,json(request)是异步Promise,会被加入微任务队列,不会立即执行

2. 关键差异点

无错误代码场景

  • fileHandle.write('[1]')是异步操作(返回Promise),未使用await等待其完成。这个操作会触发Node.js内部对文件句柄的引用计数增加,同时该Promise的决议任务会被加入微任务队列,且排在json(request).then()的前面。
  • 当.then()回调执行完毕后,代码回到await fileHandle.read()之后,继续执行到finally块的fileHandle.close()。但此时fileHandle.write('[1]')的微任务尚未执行,Node.js内部会保证:只要文件句柄还有未完成的异步操作,close()不会立即关闭文件,而是等待所有操作完成后再真正释放句柄。
  • 等json(request)的Promise决议完成、执行.then()里的write('[2]')时,文件句柄仍处于未关闭状态,因此不会报错。

报错代码场景

  • 没有fileHandle.write('[1]'),.then()内部仅触发了json(request)这个异步Promise,它被加入微任务队列。
  • .then()回调执行完毕后,代码直接走到finally块的fileHandle.close(),此时没有其他未完成的文件操作,close()会立即关闭文件句柄。
  • 后续json(request)的Promise决议完成,执行write('[2]')时,文件已经被关闭,因此抛出Error: file closed。

3. 补充说明

  • fileHandle.close()本身也是异步操作(返回Promise),代码中未使用await等待其完成,这是问题的根源之一。无错误代码能运行只是巧合,并非正确写法。
  • Node.js文档的提示是合理的:未等待Promise决议就多次调用write会导致异步操作顺序不可控,极易出现文件状态不一致的问题,正确做法是使用fileHandle.createWriteStream(),或者严格await每一个文件操作。

内容的提问来源于stack exchange,提问作者coding_chicken

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 03:26:06