You're Doing Node.js Wrong! Avoid Synchronous Code.


Dynamic Languages Traps

Node.js is one of many dynamic languages which will provide you many different options for doing the same things, some options will be better than the other for specific use cases but in Node.js anything synchronous is plain wrong when you expect to serve more than one user simultaneously.

command-line or single user scripts, no need to read further.

An example of Node.js doing this we can look at almost all file system operation APIs. These mostly have both asynchronous and synchronous versions, such as writeFile and writeFileSync.

By design, Node.js is single threaded.

Node.js is executed in a single thread inheritly due to JavaScript itself being designed that way. Anytime a synchronous operation is executed the Node.js thread must wait, any concurrent requests will be blocked until that synchronous blocking operation completes before they themselves may continue.

Consider your concurrent requests are queing to execute that same synchronous operation, such as a logging tool, even though it is not long running it is blocking and a massive performance hit.

Now imagine long running synchronous operations blocking those concurrent requests..

Node.js main event loop runs single-threaded, but many event operations run out of a thread pool.

Looking Deeper into Node.js Asynchrony

The Node.js asynchronous event loop is better for scalability in a highly concurrent environment than other languages that attempt either asynchrony or high concurrency because while worker threads are involved with Node.js, each carry out one and only one operation at any time asynchronously.
This makes the resource footprint lower than a multi-threaded scenario, mainly since less threads are required and the request is handled only once by one operation.

The Event Loop never blocks.

Any operation for each request is delegated to an event handler by the main event loop immediately after the event fires. That event is picked up by a worker thread in the thread pool and then calls back to the main event loop which send the request to the requester.

Aaron Stannard has an excellent diagram to explain this;

Node.js processing model

Unfortunately it is still possible to inadvertently make synchronous blocking calls to the worker thread using modules or purposely using native Node.js API methods.

Doing this will prevent the delegated worker thread handling concurrent operations forcing the main event loop to queue requests until the thread pool has a free worker thread to take the latest request.

Remember: A CPU Thread Cannot Be Non-blocking

While Node.js main event loop is running on a single thread to handle requests, delegations, and the callbacks is perfectly performant as long as your application will have only short-running operations that are waiting for I/O (networking, filesystem or database) to return from the worker thread.

For most web services calls this is fine, though sometimes some requests will need some CPU calculus.

When this happens Node.js main event loop thread will lock your entire server during the time this CPU intensive request is running, until it terminates.

Node.js is not designed to handle CPU intensive workload

Node.js handles requests with a single thread concurrently by multiplexing and sequencing all your js logic in a single stream of execution which works well for IO-bound workloads.


When you choose to write synchronous code in Node.js, the impact on performance is dramatic.

Node.js Ninja Style

var fs = require('fs');
fs.writeFile('server.log', 'Some text', function (err) {
  console.log('Im Non-Blocking!');

Doing it wrong

var fs = require('fs');
fs.writeFileSync('server.log', 'All requests blocked.');
console.log('It saved but..');
Posted on
Tagged in nodejs , synchronous , asynchronous , event loop

Chris is a Developer passionate about Node.js specialising in PHP7, JavaScript, and Python. Working with the newest technologies.