How to read files quickly in JavaScript

Suppose you need to read several files on a server using JavaScript. There are many ways to read files in JavaScript with a runtime like Node.js. Which one is best? Let us consider the various approaches.

Using fs.promises

const fs = require('fs/promises');
const readFile = fs.readFile;
readFile("lipsum.txt", { encoding: 'utf-8' })
.then((data) => {...})
.catch((err) => {...})

Using fs.readFile and 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) => {...})

Using fs.readFileSync

const fs = require('fs');
const readFileSync = fs.readFileSync;
var data = readFileSync("lipsum.txt", { encoding: 'utf-8' })

Using await fs.readFileSync

const fs = require('fs');
const readFileSync = fs.readFileSync;
async function f(name, options) {
  return readFileSync(name, options);
}

Using fs.readFile

const fs = require('fs');
const readFile = fs.readFile;
fs.readFile('lipsum.txt', function read(err, data) {...});

Benchmark

I wrote a small benchmark where I repeated read a file from disk. It is a simple loop where the same file is accessed each time. I report the number of milliseconds needed to read the file 50,000 times.  The file is relatively small (slightly over a kilobyte). I use a large server with dozens of Ice Lake Intel cores and plenty of memory. I use Node.js 20.1 and Bun 1.0.14. Bun is a competing JavaScript runtime.

I ran the benchmarks multiple times, and I report the best results in all cases. Your results will differ.

time (Node.js) time (Bun)
fs.promises 2400 ms 110 ms
fs.readFile and util.promisify 1500 ms 180 ms
fs.readFileSync 140 ms 140 ms
await fs.readFileSync 220 ms 180 ms
fs.readFile 760 ms 90 ms

At least on my system, in this test, the fs.promises is significantly more expensive than anything else when using Node.js. The Bun runtime is much faster than Node.js in this test.

The results are worse than they appear for fs.promises in the following sense. I find that readFileSync uses 300 ms of CPU time whereas fs.promises uses 7 s of CPU time. That is because fs.promises triggers work on several cores during the benchmark.

Increasingly the file size to, say, 32kB, does not change the conclusion. If you go to significantly larger files, many of the Node.js cases fail with “heap limit Allocation failed”. Bun keeps going even with large files. The test results do not change the conclusion with Bun: fs.readFile is consistently faster in my tests, even for large files.

Credit. My benchmark is inspired by a test case provided by Evgenii Stulnikov.

Daniel Lemire, "How to read files quickly in JavaScript," in Daniel Lemire's blog, March 12, 2024.

Published by

Daniel Lemire

A computer science professor at the University of Quebec (TELUQ).

2 thoughts on “How to read files quickly in JavaScript”

  1. Great benchmark here and I am surprised of Bun speed here, very interesting. I am a bit confused on why testing “await fs.readFileSync” – wrapping a synchronous function inside an async function doesn’t make of course its execution asynchronous, it will defer the execution of the synchronous readFileSync to the next tick in the event loop, but the main thread will be blocked while the file is being read. Maybe clarifying that this is to test just the cost difference of creating a Promise between node and bun would be clearer, if this was the intent, as I believe

  2. The audience of this blog would probably appreciate links to things like Bun. This is the first I’ve heard of it.

Leave a Reply

Your email address will not be published.

You may subscribe to this blog by email.