How to fix event loop blocking in Node.js

Event loop blocking is one of the most common performance issues in Node.js, causing unresponsive servers and poor request handling capacity. As the creator of CoreUI with over 12 years of Node.js development experience since 2014, I’ve optimized numerous APIs suffering from blocked event loops. The event loop handles all asynchronous operations, and blocking it with CPU-intensive synchronous code prevents Node.js from processing other requests. The solution involves identifying blocking operations and either making them asynchronous or offloading them to worker threads.

Identify blocking operations using profiling tools and move CPU-intensive tasks to worker threads.

// BAD: Blocking synchronous operation
app.get('/process', (req, res) => {
  const data = JSON.parse(fs.readFileSync('large-file.json', 'utf8')) // Blocks event loop
  const result = processData(data) // CPU-intensive
  res.json(result)
})

// GOOD: Non-blocking with async/await
app.get('/process', async (req, res) => {
  const data = JSON.parse(await fs.promises.readFile('large-file.json', 'utf8'))
  const result = await processDataAsync(data)
  res.json(result)
})

// BETTER: Offload to worker thread for CPU-intensive work
const { Worker } = require('worker_threads')

app.get('/process', async (req, res) => {
  const worker = new Worker('./process-worker.js', {
    workerData: { file: 'large-file.json' }
  })

  worker.on('message', (result) => {
    res.json(result)
  })

  worker.on('error', (err) => {
    res.status(500).json({ error: err.message })
  })
})

Create process-worker.js:

const { parentPort, workerData } = require('worker_threads')
const fs = require('fs').promises

async function process() {
  const data = JSON.parse(await fs.readFile(workerData.file, 'utf8'))
  const result = heavyComputation(data) // Runs in separate thread
  parentPort.postMessage(result)
}

process()

Best Practice Note

Use clinic.js doctor to detect event loop delays and identify blocking operations. Keep synchronous operations under 10ms to maintain responsiveness. For regex operations on user input, use timeouts to prevent ReDoS attacks. Break large loops into smaller chunks using setImmediate to yield to the event loop. This is exactly how we optimize CoreUI backend services—profiling endpoints, moving image processing to workers, and ensuring sub-millisecond event loop latency under load.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.

Answers by CoreUI Core Team