查看: 241|回复: 0

Node.js fs文件读取四种方案与async/await优化

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
Node.js 的 fs 是内置文件系统模块,负责文件读写和目录操作。由于 JavaScript 是单线程语言,文件 IO 的写法会直接影响主线程阻塞情况与代码维护成本。原文围绕同一个读取需求,给出了同步阻塞、回调异步、Promise 链式调用和 async/await 四种方案,并梳理了从回调地狱到线性书写的演进路线。下面按代码实现、参数含义、错误处理和适用场景重新整理。

一、模块导入方式

基础 fs 模块同时提供同步 API 和回调风格的异步 API:
  1. import fs from 'fs';
复制代码

fs/promises 专门提供 Promise 风格 API,更适合配合 async/await 使用,也是原文推荐的生产环境写法:
  1. import fs from 'fs/promises';
复制代码

两种导入方式对应不同的调用风格。基础 fs 适合沿用错误优先回调,fs/promises 则让 readFile 直接返回 Promise。

二、方案 1:readFileSync 同步读取

readFileSync 的代码顺序执行,读取期间会阻塞整个线程。它的优点是写法极简,缺点是面对接口请求或循环中的大量 IO 时会严重降低服务性能。原文明确建议,只把它用于项目启动时一次性读取配置文件。
  1. import fs from 'fs';
  2. const syncData = fs.readFileSync('./test.txt', 'utf-8');
  3. console.log(syncData);
复制代码

这里的第二个参数 'utf-8' 指定编码,返回内容直接是字符串。由于是同步调用,不需要回调,也不需要 await,但代价是主线程被占用。

三、方案 2:readFile 回调异步

readFile 采用 Node 统一的错误优先回调规范,回调形式为 (err, data) => {}。读取成功时 err 为 null,data 是文件内容;读取失败时 err 为错误对象,可能对应文件不存在、权限不足等情况。
  1. import fs from 'fs';
  2. fs.readFile('./file1.txt', 'utf-8', (err, data) => {
  3.   if (!err) {
  4.     console.log('file1', data);
  5.   } else {
  6.     console.log(err);
  7.   }
  8.   fs.readFile('./file2.txt', 'utf-8', (err, data) => {
  9.     if (!err) {
  10.       console.log('file2', data);
  11.     } else {
  12.       console.log(err);
  13.     }
  14.     fs.readFile('./file3.txt', 'utf-8', (err, data) => {
  15.       if (!err) {
  16.         console.log('file3', data);
  17.       } else {
  18.         console.log(err);
  19.       }
  20.     })
  21.   })
  22. })
复制代码

回调方式不会阻塞线程,也不需要额外导入 Promise 风格模块。但串行读取多个文件时会形成多层嵌套,也就是常说的回调地狱,后续维护和错误处理成本都很高。

四、方案 3:Promise then 链式调用

改用 fs/promises 后,readFile 返回 Promise,可以通过 .then() 链式串行执行,从而消除多层回调嵌套。原文示例在每一步返回下一个 readFile 调用,让后续 then 接收新的文件内容。
  1. import fs from 'fs/promises';
  2. fs.readFile('./file1.txt', 'utf-8')
  3.   .then(data => {
  4.     console.log('file1', data);
  5.     return fs.readFile('./file2.txt', 'utf-8');
  6.   })
  7.   .then(data => {
  8.     console.log('file2', data);
  9.     return fs.readFile('./file3.txt', 'utf-8');
  10.   })
  11.   .then(data => {
  12.     console.log('file3', data);
  13.   })
复制代码

这种写法已经解决了嵌套问题,但在多文件场景下仍要重复书写 then,代码相对冗余。

五、方案 4:async/await 生产首选

async/await 是 Promise 的语法糖,底层依旧异步,不会阻塞主线程。它的优势是代码可以像同步逻辑一样自上而下书写,可读性最强。需要注意的是,await 只能写在 async 函数内部,顶层直接使用 await 会报错,因此原文用立即执行函数包裹:
  1. import fs from 'fs/promises';
  2. (async () => {
  3.   const file1Data = await fs.readFile('./file1.txt', 'utf-8');
  4.   console.log('file1', file1Data);
  5.   const file2Data = await fs.readFile('./file2.txt', 'utf-8');
  6.   console.log('file2', file2Data);
  7.   const file3Data = await fs.readFile('./file3.txt', 'utf-8');
  8.   console.log('file3', file3Data);
  9. })();
复制代码

实际工程中还应增加 try/catch 捕获读取错误。原文给出的完整健壮写法如下:
  1. import fs from 'fs/promises';
  2. (async () => {
  3.   try {
  4.     const data = await fs.readFile('./test.txt', 'utf-8');
  5.     console.log(data);
  6.   } catch (err) {
  7.     console.error('文件读取失败:', err);
  8.   }
  9. })();
复制代码

原文还补充了一个异步执行顺序细节:Promise、await 的后续执行属于微任务,setTimeout 属于宏任务。理解这一点,有助于排查文件读取与其他定时逻辑混用时的输出顺序问题。

六、四种方式对比

readFileSync:阻塞线程,写法极简,但大量 IO 会严重影响服务性能。

readFile 回调:非阻塞,无需额外导入,但串行读取多文件会产生回调地狱。

Promise then:非阻塞,无嵌套结构,但多文件链式调用时代码重复繁琐。

async/await:非阻塞,线性阅读、代码整洁,属于 ES8 语法,需要 async 函数包裹。

从演进路线看,就是同步阻塞、回调函数、Promise then 链式、async/await 逐步过渡的过程,目标是在不阻塞主线程的前提下降低嵌套和维护成本。

七、工程开发最佳实践

1. 业务文件读写统一使用 fs/promises 配合 async/await,避免回调嵌套。

2. 所有文件读取都必须搭配 try/catch 捕获异常,不能只处理成功分支。

3. readFileSync 仅用于项目初始化加载配置,接口业务中禁止使用,防止阻塞主线程。

4. 路径搭配 path.resolve 生成绝对路径,防止运行目录变更导致文件找不到。

整体来看,fs 模块的四种读取方案并不是简单的 API 替换,而是围绕单线程模型做出的异步编程选择。理解每种写法是否阻塞、错误如何传递、串行代码如何组织,才能在实际项目中选对方案并减少调试成本。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-27 13:12 , Processed in 0.019878 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部