Sitemap

10 Common Mistakes with Synchronous Code in Node.js

4 min readMar 11, 2025

--

Press enter or click to view image in full size
10 Common Mistakes with Synchronous Code in Node.js
10 Common Mistakes with Synchronous Code in Node.js

Node.js is built on the idea of asynchronous, non-blocking I/O, making it super efficient for web servers and APIs. But sometimes, developers fall into the trap of using synchronous code incorrectly, leading to performance bottlenecks, scalability issues, and unexpected bugs.

1. Blocking the Event Loop with Synchronous Code

Mistake:

Using synchronous functions like fs.readFileSync() or crypto.randomBytesSync() inside an API handler.

const fs = require('fs');

function handleRequest(req, res) {
const data = fs.readFileSync('./largeFile.txt', 'utf-8');
res.send(data);
}

This blocks the event loop, preventing other requests from being handled until the file is fully read.

Fix:

Use the async version to keep the event loop free:

const fs = require('fs/promises');

async function handleRequest(req, res) {
const data = await fs.readFile('./largeFile.txt', 'utf-8');
res.send(data);
}

Now, while Node.js reads the file, the server can handle other requests instead of waiting idly.

2. Using for Loops Instead of Async Iteration

Mistake:

Mixing synchronous loops with asynchronous code.

const users = ['Alice', 'Bob', 'Charlie'];

for (let i = 0; i < users.length; i++) {
console.log(await getUserData(users[i])); // ❌ BAD
}

Each iteration waits for the previous one to complete, making the loop slow.

Fix:

Use Promise.all() to run operations in parallel:

await Promise.all(users.map(async (user) => {
console.log(await getUserData(user));
}));

Always parallelize async operations when possible.

3. Blocking the Main Thread with CPU-Heavy Work

Mistake:

Performing expensive calculations inside the event loop:

function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}

app.get('/compute', (req, res) => {
res.send({ result: fibonacci(40) });
});

A single request will block all other requests while computing.

Fix:

Use worker threads to offload heavy computations:

const { Worker } = require('worker_threads');

app.get('/compute', (req, res) => {
const worker = new Worker('./worker.js', { workerData: 40 });

worker.on('message', (result) => res.send({ result }));
worker.on('error', (err) => res.status(500).send(err));
});

Now, the event loop remains free, and requests don’t get stuck.

4. Using JSON.parse() on Large Payloads in Sync Mode

Mistake:

Parsing large JSON objects synchronously blocks the event loop:

app.post('/upload', (req, res) => {
const data = JSON.parse(req.body); // ❌ Blocking
res.send({ success: true });
});

Fix:

Use streaming JSON parsers (stream-json package) to handle large payloads efficiently:

const { parser } = require('stream-json');
app.post('/upload', (req, res) => {
req.pipe(parser()).on('data', (chunk) => {
console.log(chunk);
});
res.send({ success: true });
});

5. Incorrect Use of await in Loops

Mistake:

Placing await inside a loop unnecessarily slows down execution.

for (const id of userIds) {
const user = await getUserById(id); // ❌ Slow, runs one-by-one
console.log(user);
}

Fix:

Run them in parallel:

const users = await Promise.all(userIds.map(getUserById));
console.log(users);

6. Relying on require() for Large Modules in Loops

Mistake:

Calling require() inside a function repeatedly:

function handleRequest(req, res) {
const crypto = require('crypto'); // ❌ Loading every time
res.send(crypto.randomBytes(16).toString('hex'));
}

Fix:

Move require() outside to load once:

const crypto = require('crypto');

function handleRequest(req, res) {
res.send(crypto.randomBytes(16).toString('hex'));
}

require() is synchronous and caches modules, so don’t call it inside functions.

7. Using Sync Database Calls in an API Endpoint

Mistake:

Running a database query synchronously:

const db = require('some-db-lib');

app.get('/user/:id', (req, res) => {
const user = db.getUserSync(req.params.id); // ❌ Blocking
res.send(user);
});

Fix:

Use async versions:

app.get('/user/:id', async (req, res) => {
const user = await db.getUser(req.params.id);
res.send(user);
});

8. Reading Large Files with fs.readFileSync()

Mistake:

Loading an entire file synchronously:

const data = fs.readFileSync('large-file.txt'); // ❌ Blocking
console.log(data);

Fix:

Stream the file instead:

fs.createReadStream('large-file.txt').pipe(process.stdout);

This prevents memory bloat and keeps the event loop responsive.

9. Handling HTTP Requests Synchronously

Mistake:

Making blocking HTTP requests:

const request = require('sync-request');

app.get('/data', (req, res) => {
const response = request('GET', 'https://api.example.com/data'); // ❌ Blocking
res.send(response.getBody('utf8'));
});

Fix:

Use axios or fetch() for async requests:

const axios = require('axios');

app.get('/data', async (req, res) => {
const response = await axios.get('https://api.example.com/data');
res.send(response.data);
});

10. Not Using process.nextTick() for Immediate Callbacks

Mistake:

Using setTimeout(fn, 0) when an immediate callback is needed.

Fix:

Use process.nextTick():

process.nextTick(() => {
console.log('Executed immediately after the current operation.');
});

Conclusion

Synchronous code in Node.js isn’t inherently bad, but misusing it can slow down your application and introduce bottlenecks. To avoid common mistakes:

→ Always use async versions of file and database operations.
Parallelize async operations with Promise.all().
→ Offload CPU-heavy tasks to worker threads.
→ Use streams for large data handling.
→ Keep the event loop free for better performance.

You may also like:

1) 5 Common Mistakes in Backend Optimization

2) 7 Tips for Boosting Your API Performance

3) How to Identify Bottlenecks in Your Backend

4) 8 Tools for Developing Scalable Backend Solutions

5) 5 Key Components of a Scalable Backend System

6) 6 Common Mistakes in Backend Architecture Design

7) 7 Essential Tips for Scalable Backend Architecture

8) Token-Based Authentication: Choosing Between JWT and Paseto for Modern Applications

9) API Rate Limiting and Abuse Prevention Strategies in Node.js for High-Traffic APIs

10) Can You Answer This Senior-Level JavaScript Promise Interview Question?

11) 5 Reasons JWT May Not Be the Best Choice

12) 7 Productivity Hacks I Stole From a Principal Software Engineer

13) 7 Common Mistakes in package.json Configuration

Read more blogs from Here

Share your experiences in the comments, and let’s discuss how to tackle them!

Follow me on LinkedIn

--

--

Responses (1)