Node.js fs 文件系统模块 四种读写方案全解析

fs 是 Node 内置文件系统模块,用于读写文件、操作文件夹。JS 是单线程语言,fs 提供同步阻塞、回调异步、Promise、async/await 四种写法,一步步解决异步嵌套带来的回调地狱问题。

一、模块导入两种方式

  1. 基础 fs:提供同步 API、回调风格异步 API

js

运行

javascript 复制代码
import fs from 'fs';
  1. fs/promises:专门提供 Promise 风格 API,适配 async/await(推荐生产使用)

js

运行

javascript 复制代码
import fs from 'fs/promises';

二、方案 1:同步读取 readFileSync(阻塞主线程)

特点:代码顺序执行,文件读取期间阻塞整个线程,不适合接口、循环大量 IO;仅适合项目启动一次性读取配置文件。

js

运行

ini 复制代码
import fs from 'fs';
const syncData = fs.readFileSync('./test.txt', 'utf-8');
console.log(syncData);

缺点:并发场景大量文件读写会严重降低服务性能。

三、方案 2:回调异步 readFile(ES6 原生写法,回调地狱)

Node 统一错误优先回调规范:(err, data) => {}

  • 读取成功:err = null,data 为文件内容
  • 读取失败:err 为错误对象(文件不存在、权限不足等)

串行读取多个文件时会产生多层嵌套,即回调地狱,维护成本极高。

js

运行

javascript 复制代码
import fs from 'fs';
fs.readFile('./file1.txt', 'utf-8', (err, data) => {
  if (!err) {
    console.log('file1', data);
  } else {
    console.log(err);
  }
  fs.readFile('./file2.txt', 'utf-8', (err, data) => {
    if (!err) {
      console.log('file2', data);
    } else {
      console.log(err);
    }
    fs.readFile('./file3.txt', 'utf-8', (err, data) => {
      if (!err) {
        console.log('file3', data);
      } else {
        console.log(err);
      }
    })
  })
})

四、方案 3:Promise 链式调用(消除嵌套)

基于 fs/promises,readFile 返回 Promise,使用 .then() 链式串行执行,解决多层嵌套问题;缺点是多文件场景重复书写 then,代码冗余。

js

运行

kotlin 复制代码
import fs from 'fs/promises';
fs.readFile('./file1.txt', 'utf-8')
  .then(data => {
    console.log('file1', data);
    return fs.readFile('./file2.txt', 'utf-8');
  })
  .then(data => {
    console.log('file2', data);
    return fs.readFile('./file3.txt', 'utf-8');
  })
  .then(data => {
    console.log('file3', data);
  })

五、方案 4:async/await(ES8 最优方案,生产首选)

async/await 是 Promise 的语法糖,底层依旧异步、不会阻塞主线程;代码线性自上而下书写,可读性最强。限制:await 只能写在 async 函数内,顶层直接 await 会报错,使用 IIFE 立即执行函数包裹。补充:Promise、await 属于微任务;setTimeout 属于宏任务。

js

运行

javascript 复制代码
import fs from 'fs/promises';
(async () => {
  const file1Data = await fs.readFile('./file1.txt', 'utf-8');
  console.log('file1', file1Data);
  const file2Data = await fs.readFile('./file2.txt', 'utf-8');
  console.log('file2', file2Data);
  const file3Data = await fs.readFile('./file3.txt', 'utf-8');
  console.log('file3', file3Data);
})();

完整健壮写法(增加 try/catch 捕获读取错误)

js

运行

javascript 复制代码
import fs from 'fs/promises';
(async () => {
  try {
    const data = await fs.readFile('./test.txt', 'utf-8');
    console.log(data);
  } catch (err) {
    console.error('文件读取失败:', err);
  }
})();

六、异步代码演进路线

同步阻塞(性能差)→ 回调函数(回调地狱)→ Promise then 链式(冗余)→ async/await(简洁易维护)

七、四种读取方式对比表

表格

写法 是否阻塞线程 优点 缺点
readFileSync 阻塞 写法极简 大量 IO 严重影响服务性能
readFile 回调 非阻塞 无需额外导入 串行多文件产生回调地狱
Promise then 非阻塞 无嵌套结构 多文件链式代码重复繁琐
async await 非阻塞 线性阅读,代码整洁 ES8 语法,需 async 函数包裹

八、工程开发最佳实践

  1. 业务文件读写统一使用 fs/promises + async/await;
  2. 所有文件读取必须搭配 try/catch 捕获异常;
  3. 同步 readFileSync 仅用于项目初始化加载配置,接口业务禁止使用;
  4. 路径搭配 path.resolve 生成绝对路径,防止运行目录变更导致文件找不到。
相关推荐
明月_清风30 分钟前
拆解 DeepSeek 最新开源推理框架 DeepInfer:万亿级大模型的高效落地实战
人工智能·后端·deepseek
Thneonl41 分钟前
选举之后,数据去哪了:MongoDB rollback 目录里的失踪写入
后端·架构
Thneonl1 小时前
一个 user_id 打爆 Prometheus
后端·架构
弦儿1 小时前
从零认识 SpringBoot Web 后端:HTTP 协议 + 三层架构 + IOC/DI 知识点全梳理(附全套代码)
后端
海豚调度1 小时前
Apache DolphinScheduler 2026 年 9 月社区贡献总结报告
后端
全栈弄潮儿1 小时前
Python实战第10期:异常处理
后端·python·编程语言
专业程序开发源1 小时前
springboot校企合作双选会一站式预约与面试系统16010-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·面试·php·课程设计
郑州光合科技余经理2 小时前
同城商城源码:商品库和订单库怎么随包交付
android·java·开发语言·后端·uni-app·php·ai编程
邱杰4703 小时前
数智人事系统(十七):加班核算与薪资构成
后端·github