在JavaScript中,尤其是配合Node.js这样的运行时环境,有多种方式可以读取服务器上的文件。每种方法都有其适用场景和优缺点。今天,我们就来比较几种常见的文件读取方法,看看哪一种最适合你的需求。
太长不看版
如果你在寻找一个快速且简单的方法,fs.promises是一个不错的选择,它提供了一个Promise接口,让你能够以现代的异步语法来处理文件读取。然而,如果你更倾向于同步方法,你可能会更喜欢fs.readFileSync,虽然它会阻塞当前线程直到文件读取完成。最后,如果你在寻找一种能够提供更好的性能的方法,你可能会想要考虑使用fs.readFile,这是Node.js中最传统的异步文件读取方法,使用回调函数来处理结果。
使用fs.promises
const fs = require('fs/promises');
const readFile = fs.readFile;
readFile("lipsum.txt", { encoding: 'utf-8' })
.then((data) => {...})
.catch((err) => {...});
这种方法使用了Node.js提供的fs.promises接口,它返回一个Promise对象,可以链式调用.then()和.catch()方法来处理异步操作的结果。
使用fs.readFile和util.promisify
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);
readFile("lipsum.txt", { encoding: 'utf-8' })
.then((data) => {...})
.catch((err) => {...});
这种方法通过util.promisify将传统的回调函数转换成返回Promise的函数,使得你可以用更现代的异步语法来处理文件读取。
使用fs.readFileSync
const fs = require('fs');
const readFileSync = fs.readFileSync;
var data = readFileSync("lipsum.txt", { encoding: 'utf-8' });
这是同步方法,它会阻塞当前线程直到文件读取完成,适用于对性能要求不高且文件不大的情况。
使用await和fs.readFileSync
const fs = require('fs');
const readFileSync = fs.readFileSync;
async function f(name, options) {
return readFileSync(name, options);
}
这种方法结合了async/await语法和同步读取,可以在需要同步行为但希望代码看起来更异步的情况下使用。
使用fs.readFile
const fs = require('fs');
const readFile = fs.readFile;
readFile('lipsum.txt', function read(err, data) {...});
这是Node.js中最传统的异步文件读取方法,使用回调函数来处理结果。
性能比较
我进行了一项小的基准测试,重复读取磁盘上的同一个文件,并记录读取文件50,000次所需的毫秒数。文件相对较小,略多于一千字节。我使用的是一台拥有数十个Ice Lake Intel核心和大量内存的大型服务器。测试使用了Node.js 20.1和Bun 1.0.14。Bun是一个与Node.js竞争的JavaScript运行时[1]。
多次运行基准测试,我报告了所有情况下的最佳结果。你的结果可能会有所不同。
方法 | Node.js 时间 | Bun 时间 |
fs.promises | 2400 毫秒 | 110 毫秒 |
fs.readFile 和 util.promisify | 1500 毫秒 | 180 毫秒 |
fs.readFileSync | 140 毫秒 | 140 毫秒 |
await fs.readFileSync | 220 毫秒 | 180 毫秒 |
fs.readFile | 760 毫秒 | 90 毫秒 |
至少在我的系统上,在这次测试中,使用Node.js时fs.promises明显比其他方法成本更高。Bun运行时在这次测试中比Node.js快得多。
结果看起来对fs.promises更不利,因为readFileSync使用了300毫秒的CPU时间,而fs.promises使用了7秒的CPU时间。这是因为fs.promises在基准测试期间触发了多个核心的工作。
将文件大小增加到32kB并不会改变结论。如果你使用更大的文件,许多Node.js的案例会因“堆限制Allocation failed”而失败。Bun即使在大文件下也能继续工作。测试结果在Bun中没有改变结论:在我的测试中,即使对于大文件,fs.readFile也是一致更快的。
本文译自:https://lemire.me/blog/2024/03/12/how-to-read-files-quickly-in-javascript/
Reference
[1]Bun是一个与Node.js竞争的JavaScript运行时: https://bun.dev/