readFileSync and its danger
1. What readFileSync does
import fs from 'fs';
const data = fs.readFileSync('./data.txt', 'utf8');
console.log(data);fs.readFileSync() reads a file synchronously, meaning it blocks program execution until the operation fully finishes.
Node.js won't move to the next line of code until the file has been read.
2. Node.js is single-threaded
Here's the main reason synchronous I/O is dangerous:
Node.js runs on a single thread (the event loop). While one operation runs synchronously, the entire thread is blocked.
That means:
- while
readFileSync()reads the file, - no other requests or events get handled: not HTTP requests, not timers, not event handlers.
3. What actually happens with readFileSync()
The asynchronous version (fs.readFile)
Event Loop:
→ Sent the "read the file" task to a thread (the thread pool)
→ Freed up and can serve other requests
→ Calls the callback once the file is readyThe synchronous version (fs.readFileSync)
Event Loop:
→ Started reading the file
⏸ Stopped, waiting for the result
Does nothing else
→ Only continues once the operation finishes4. An example of the problem
server.js
import http from 'http';
import fs from 'fs';
const server = http.createServer((req, res) => {
const data = fs.readFileSync('./bigfile.txt', 'utf8');
res.end(data);
});
server.listen(3000);What happens:
- The first request takes 2 seconds to read the file, everything else stops;
- Other requests wait;
- The server "freezes" under load.
The correct version (asynchronous)
import http from 'http';
import { readFile } from 'fs/promises';
const server = http.createServer(async (req, res) => {
const data = await readFile('./bigfile.txt', 'utf8');
res.end(data);
});
server.listen(3000);Now Node can:
- read the file,
- and serve other requests from the event queue, at the same time.
5. An example of a "dangerous" situation
for (let i = 0; i < 1000; i++) {
fs.readFileSync('./data.txt', 'utf8');
}Here, each operation waits for the previous one. If the file is large, the app "freezes" for seconds or minutes.
Whereas the async version:
Promise.all(
Array(1000).fill().map(() => readFile('./data.txt', 'utf8'))
);reads the files in parallel (in the thread pool).
6. Performance in numbers
| Method | Time (roughly) | Blocks the thread |
|---|---|---|
readFileSync() | 100-300 ms per file | Yes |
readFile() | 100-300 ms (in the background) | No |
readFile (via fs/promises) | 100-300 ms (in the background) | No |
On a server handling 1,000 requests per second,
every readFileSync() call can freeze the thread and turn Node.js from a high-performance system into a single-threaded bottleneck.
7. When readFileSync() is acceptable
OK to use:
- In scripts, CLI tools, migrations (where requests don't compete for the thread);
- At app startup to load configuration;
- In tests, where you just need to fetch data;
- When performance isn't critical.
Don't use it:
- In HTTP servers, Express/Fastify routes;
- In APIs or event handlers;
- In async queues;
- In production with concurrent users.
8. An analogy
Imagine Node.js as a waiter:
readFile(), they hand the order to the kitchen and go serve other customers;readFileSync(), they just stand there and wait for the chef to finish the dish.
9. How to correctly replace readFileSync()
| Before (bad) | After (good) |
|---|---|
fs.readFileSync() | await fs.promises.readFile() |
fs.writeFileSync() | await fs.promises.writeFile() |
fs.existsSync() | await fs.promises.access() |
Quick summary
| Point | readFileSync() | readFile() / fs.promises.readFile() |
|---|---|---|
| Execution | Synchronous | Asynchronous |
| Blocks the thread | Yes | No |
| Good for a server | No | Yes |
| Simplicity | Yes | Yes (via await) |
| Performance | Poor under load | Excellent |
In one sentence
fs.readFileSync()is dangerous in server applications because it blocks the entire event loop, keeping Node.js from handling other requests, and turns an asynchronous server into a "frozen" single-threaded process.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.