Async vs sync code

By

Understand the difference between synchronous and asynchronous code, and why async APIs let Node.js handle more traffic than blocking languages like PHP.

~~~

Synchronous code runs one operation at a time: each line blocks the program until it’s done. Asynchronous code starts an operation, lets the rest of the program keep running, and gets called back when the result is ready.

You might have heard that Node.js is fast because it provides asynchronous APIs for all expensive operations, like network access or filesystem.

What does having an asynchronous API mean?

If you anticipate an operation can take a lot of time, it makes sense to run it asynchronously, so other code can run in the meantime, and have a hook that’s called when that operation ends.

Let’s see the difference in code

Node.js offers both versions for reading a file. This is the synchronous one:

const fs = require('fs')

const data = fs.readFileSync('/Users/flavio/notes.txt', 'utf8')
console.log(data)
console.log('done')

Nothing else runs while the file is being read. First the file content prints, then done.

This is the asynchronous version:

const fs = require('fs')

fs.readFile('/Users/flavio/notes.txt', 'utf8', (err, data) => {
  if (err) {
    console.error(err)
    return
  }
  console.log(data)
})
console.log('done')

Here done prints first. The program didn’t stop and wait for the disk. It registered a callback, moved on, and the callback ran later, when the file data was ready.

Why does this matter so much?

Node.js runs your JavaScript on a single thread. If that thread is blocked waiting for a file or a network response, nothing else happens: no other request is served, no timer fires.

This is how Node.js can handle a lot more traffic than, say, PHP or Rails without using async libraries.

What usually happens in PHP or Python code is that the thread blocks until the sync operation (reading from the network, writing a file..) ends. The server compensates by running many threads or processes, one per request, which costs memory.

If the code runs asynchronously, the CPU is not idle waiting for the process to complete. It can go on with other tasks queued up until the original operation is ready to move on.

Most programming languages that were not traditionally async today do have 3rd party libraries that implement ways to call asynchronous code.

A common pitfall

The sync versions look tempting because the code reads top to bottom. But be careful where you use them.

Calling fs.readFileSync() inside an HTTP server request handler blocks the entire server for the duration of the read. Every other client waits. With one slow file and some traffic, response times fall apart.

The fix: in a server, always use the asynchronous versions. Sync APIs are fine in command line scripts, where there’s nobody else waiting.

~~~

Related posts about js: