JavaScript

Synchronous vs. Asynchronous and Callbacks

Node provides an event-driven and asynchronous platform for server-side JavaScript. A synchronous function blocks until it completes its operations. An asynchronous function returns immediately and the result is passed to a handler, called callback, at a later cycle of the event loop.

What is a callback function

Callbacks are used frequently in Node development and they’re simple to use. Callbacks are functions that are invoked to propagate the result of an operation and this is exactly what we need when dealing with asynchronous operations. They replace the use of the return statement that always executes synchronously:

//Synchronous function
function someFunc (num) {
 return num++
}

JavaScript is a great language to represent callbacks, because functions are first class objects and can be easily assigned to variables, passed as arguments, returned from another function invocation, or stored into data structures.

Closures are an ideal construct for implementing callbacks. With closures, you can reference the environment in which a function was created:

//Read file asynchronously example
//using callback function

const fs = require('fs')

//fs.readFile(filePaht, callback)
fs.readFile('./a_file.txt', (err, data) => {
 // do something
});

In above example, the fs.readFile use callback with two arguments. The first argument is for an error and the second argument is for the results. Most Node built-in modules use callbacks with two arguments as demonstrated in this example.

Let’s start with callback pattern, this is the most basic and the best known pattern to deal with asynchronous programming.

Synchronous example

To clarify the concept, let’s take a look at a simple synchronous function:

function sayHelloSync (name,callback){
 callback('hello '+name)
}

The sayHelloSync function is a traditional synchronous function, it will return a value only when the callback completes its execution. The following code demonstrates this function:

console.log('before')

//sayHelloSync (name, callbackFunction)
sayHelloSync('BrainBell',(result) => {
 console.log(result)
})

console.log('after')

The preceding code will print the following:

before
hello BrainBell
after

Asynchronous example

Now, we create an asynchronous function sayHelloAsync(), we’ll simply use setTimeout() to simulate an asynchronous invocation of the callback:

function sayHelloAsync (name,callback){
 setTimeout(() => {
  callback('hello '+name)
 }, 100)
}

Now, let’s try to use this function and see how the order of the operations changes:

console.log('before')

//sayHelloAsync (name, callback)
sayHelloAsync('BrainBell',(result) => {
 console.log(result)
})

console.log('after')

The preceding code will print the following:

before
after
hello BrainBell

Since setTimeout() triggers an asynchronous operation, it will not wait anymore for the callback to be executed, but instead, it returns immediately giving the control back to sayHelloAsync(), and then back to its caller. This property in Node.js is crucial, as it allows the stack to unwind, and the control to be given back to the event loop as soon as an asynchronous request is sent, thus allowing a new event from the queue to be processed.