Logo Vincent
返回文章列表

Node.js-开发实践:高性能FS

Node.js
Node.js-开发实践:高性能FS
目录

【前言】

nodejs的fs模块相信大家都不陌生,

本文对比一下fs模块的三种使用方式。

【fs的三种使用方式】

nodejs官方提供了fs的三种使用方式,

https://nodejs.org/dist/latest-v18.x/docs/api/fs.html#promise-example

callback方式

示例代码如下:

const { unlink } = require("node:fs");

unlink("/tmp/hello", (err) => {
  if (err) throw err;
  console.log("successfully deleted /tmp/hello");
});

这是最传统的方式,

充分的体现了nodejs事件驱动,非阻塞IO的特点,

但是代码会嵌套,导致回调地狱。。

promise方式

示例代码如下:

const { unlink } = require("node:fs/promises");

(async function (path) {
  try {
    await unlink(path);
    console.log(`successfully deleted ${path}`);
  } catch (error) {
    console.error("there was an error:", error.message);
  }
})("/tmp/hello");

这种方式中,

每个api都返回了一个Promise对象,

需要配合async / await使用。

sync方式

示例代码如下:

const { unlinkSync } = require("node:fs");

try {
  unlinkSync("/tmp/hello");
  console.log("successfully deleted /tmp/hello");
} catch (err) {
  // handle the error
}

这种方式中,

不会导致代码嵌套,

而且不需要配合async / await使用。

小结

单从书写方式来说:sync > promise > callback

【测试代码】

接着会从两个维度进行测试

1. 处理文件的耗时

2. 处理文件的性能

找一个最简单的fs场景,判断文件是否存在,

三种方式对应的代码如下:

callback方式

// fs
const { access } = require("node:fs");

// is exists
exports.isExistsCallback = function (filePath, callback) {
  access(filePath, (err) => {
    if (callback) callback(err ? false : true);
  });
};

promise方式

// fs
const { access } = require("node:fs/promises");

// is exists
exports.isExistsPromise = async function (filePath) {
  try {
    await access(filePath);
    return true;
  } catch (error) {
    return false;
  }
};

sync方式

// fs
const { accessSync } = require("node:fs");

// is exists
exports.isExistsSync = function (filePath) {
  try {
    accessSync(filePath);
    return true;
  } catch (error) {
    return false;
  }
};

【时间对比】

测试方式为,传入一个不存在的路径,

重复n次,对比耗时

callback方式测试代码

const { isExistsCallback } = require("./fs-callback.js");

const times = 10000;
let callIndex = 0;
const filePath = "/file/not/exists";

// test callback
function testCallback() {
  console.time("isExists by callback");

  isExistsCallback(filePath, callback);
}
function callback(res) {
  callIndex = callIndex + 1;

  if (callIndex === times) {
    console.timeEnd("isExists by callback");
  } else {
    isExistsCallback(filePath, callback);
  }
}

promise方式测试代码

const { isExistsPromise } = require("./fs-promise.js");

const times = 10000;
const filePath = "/file/not/exists";

// test promise
async function testPromise() {
  console.time("isExists by promise");

  for (let i = 0; i < times; i++) {
    await isExistsPromise(filePath);
  }

  console.timeEnd("isExists by promise");
}

sync方式测试代码

const { isExistsSync } = require("./fs-sync.js");

const times = 10000;
const filePath = "/file/not/exists";

// test sync
function testSync() {
  console.time("isExists by sync");

  for (let i = 0; i < times; i++) {
    isExistsSync(filePath);
  }

  console.timeEnd("isExists by sync");
}

时间对比

重复1w次,效果如下:

重复10w次,效果如下:

小结

从处理文件的耗时来看:sync << ( callback < promise )

【性能对比】

这里的性能对比,

不是对比处理文件时占用的内存或者cpu,

而是指对nodejs进程的占用,

测试方式如上,只是多加了一个每1ms的打印,

let intervalIndex = 0;
const intervalId = setInterval(() => {
  console.log(1);

  intervalIndex = intervalIndex + 1;
  if (intervalIndex === 10) clearInterval(intervalId);
}, 1);

并将重复次数调整到10次,

callback方式效果

promise方式效果

sync方式效果

性能对比

这里可以看出sync的方式是阻塞的方式,

也就是虽然sync处理文件耗时比较少,

但是会阻塞之后所有的操作,直到sync执行完毕,

简单理解是为了“减少处理时间,优化js写法”,

而独占了nodejs进程,阻塞了其他操作。

小结

从对进程的占用来看:( callback < promise ) << sync

【高性能fs】

其实在web前端或者nodejs的开发中,

fs的频繁使用比较少,

一般是上传下载等场景会用到。

但是在electron开发桌面应用时,

fs的操作就会变的很频繁,

这个时候就要关注高性能的使用fs,

callback方式

1. 书写会导致回调地狱

2. 体现nodejs事件驱动,非阻塞io

3. 性能最好

promise方式

1. 书写需要配合async / await使用

2. 也体现事件驱动,非阻塞io

3. 性能和callback很接近,稍微有损耗

sync方式

1. 书写最简单, 也不需要async / await

2. 阻塞性使用

3. 性能极差,谨慎使用

小结

1. 如果是很简单的场景且追求耗时少,可以使用sync的方式

2. 其他场景都谨慎使用sync,而推荐使用promise的方式

相关推荐

Node.js-WebServer开发实践:使用PM2-Cluster模式提升接口QPS

【前言】 pm2是nodejs进程管理工具, https://pm2.keymetrics.io/ 介绍详见之前的一篇文章: https://blog.csdn.net/uikoo9/article/details/79018750 , 本文介绍下pm2的cluster模式, 并使用pm2的clus

Node.js-WebServer开发实践:使用autocannon进行接口压测

【前言】 AutoCannon是基于Node.js的接口压测工具, https://www.npmjs.com/package/autocannon 【安装】 【cli使用】 AutoCannon可以通过cli的方式使用, 其中各参数的含义可以直接输入autocannon查看, 例如10个并发连接,

Node.js-开发实践:图片处理

【前言】 使用nodejs的过程中会遇到一些处理图片的场景, 比如上传图片时进行压缩, 或者nodejs开发客户端本处理图片等, 本文介绍下nodejs常见的图片处理操作。 【常见图片处理库】 nodejs常见的图片处理库如下, 可以看到sharp从各方面都遥遥领先 npm包 github地址 gi

Node.js-开发实践:下载文件

【前言】 下载文件是Node.js中最常见的功能, 但实际开发中下载文件也会隐藏各种各样的坑。 【原始代码】 如果在网络搜索Node.js下载文件代码, 大概会搜到类似下面的代码片段, 本文从这里开始,陆续优化下载文件这个功能。 上面的代码片段可以看到: 1.兼容了Node.js原生的http和ht

Node.js-开发实践:使用健壮的FS

【前言】 fs模块是nodejs中最常见的模块, 可是fs的使用经常会有各种意想不到的坑。 【高性能FS】 其中之一是没有使用高性能的fs, 导致在electron应用中造成卡顿, fs模块有3种使用方式, callback方式 1\. 书写会导致回调地狱 2\. 体现nodejs事件驱动,非阻塞i

© 2026 vincentqiao.com . 保留所有权利。