Callback, Promise and Async

Callback

Callback is an asynchronous equivalent for a function. A callback function is called at the completion of a given task. Node makes heavy use of callbacks. All the APIs of Node are written in such a way that they support callbacks.

For example, a function to read a file may start reading file and return the control to the execution environment immediately so that the next instruction can be executed. Once file I/O is complete, it will call the callback function while passing the callback function, the content of the file as a parameter. So there is no blocking or wait for File I/O. This makes Node.js highly scalable, as it can process a high number of requests without waiting for any function to return results.

A blocking program executes very much in sequence. From the programming point of view, it is easier to implement the logic but non-blocking programs do not execute in sequence. In case a program needs to use any data to be processed, it should be kept within the same block to make it sequential execution.

// This is synchronous.

function processData() {

let data = fetchDataInAFarAwayAndMysticDatabase();

data += 1;

return data;

}

// This is asynchronous…

function processData(callback) {

fetchDataInAFarAwayAndMysticDatabase(function (err, data) {

if (err) {

return callback(err);

}

data += 1;

callback(null, data);

});

}

The cool thing about asynchronous programming is that while your code waits for something to be done (like an API call or a response from a mystic and far away database) it can do something else. In other words, your code doesn’t get blocked when a process is taking a long time. This is actually the main reason why Node.js was even created; servers running synchronous code spend a lot of time waiting. If servers can process requests while they are waiting for I/O, stuff gets done faster.

A Callback is simply a function passed as an argument to another function which will then use it (call it back).

Here is a simple, yet bold, example of a callback function.

fs.readFile(funFileName, function(err, file) {

if(err)

handleError(err);

console.log(“file: “, file)

})

When fs.readFile is done fetching the file funFileName, it executes the callback function, which handles the error if an error is thrown and logs the retrieved file to the console.

Note that the callback function is taking 2 arguments: err and file. By convention, the first argument of a callback function is an error; if an error is thrown by the parent function, it will be there for you to deal with and if no error is thrown — that happens sometimes ? — then the first argument should be null. Also by convention, the following arguments are the response data.

All that is fine and well, but why not simply write the above like below?

let file = fs.readFile(fooFileName);

console.log(“file: “, file);

In short, because async. In this second example, file will be undefined when we try to log it, because fs.readFile won’t be done fetching before we get to the console.log().

Pros:

  • Handy for single async operations. Allow easy data and error control.
  • Should work on every node version and almost all packages of node. As callbacks are functions, they don’t need any transpilers.

Cons:

  • For multiple nested async operations this creates a callback hell
  • Error handling has to be done for each operation (no global exception handler)

Promises

Promises are objects which have 3 main states — pending, resolved and rejected. Depending on the response of an async action a promise is either resolved or rejected. Multiple promises can be chained one below the other. A single catch handler at the bottom is sufficient for an error in any promise.

exports.createOrAddUserPromise = (req, res, next) => {

User.findUserPromise(req.body)

.then(user => {

if (user) {

const existingUser = new User(user);

return newUser.updatePromise(req.body)

}

const newUser = new User();

return newUser.savePromise(req.body);

})

.then(data => res.send(data))

.catch(e => next(e));

}

Pros:

  • Allows for easy chaining of async operations. Whatever is returned in the .then function, can be chained in the next .then function.
  • One catch handler at the bottom will catch an error if either of the chained promises throws an exception.

Cons:

  • Most libraries may require a promisify wrapper around it like bluebird, unless they support promises out of the box.
  • The scope of a chained function is isolated to that function itself. So some data resolved in the second chain cannot be used in the 4th chain unless a global let variable is declared.

Async / Await

Async / await at the end of the day is still a promise. It’s just a way of writing asynchronous code in a sort of synchronous manner.

Each async function has to be prefixed with async. Every asynchronous action in it has to be prefixed with the word await . Also, every async function returns a promise which can be resolved further.

exports.createOrAddUserAsync =  async (req, res, next) => {

try {

const user = await User.findUserPromise(req.body);

if (user) {

const existingUser = new User(user);

const data = await existingUser.updatePromise(req.body);

return res.send(data);

}

 

const newUser = new User();

const data = await newUser.savePromise(req.body);

return res.send(data);

} catch (e) {

return next(e)

}

}

Pros:

  • CLEAN LOOKIN CODE. I cannot stress on this point enough. All of the resolved bit can be accessed within the try block.
  • Entire block can be treated as a synchronous bit of code. (Though it is async in nature).
  • Adding try, catch to asynchronous code.
  • One unified error handler in the catch block.

Cons:

  • Node 8+ comes with async await built in. For older versions, a babel transpiler is needed for server-side code.
  • Adding the async keyword is not very intuitive.
  • Using async/await inside a promise constructor is an anti-pattern.
  • Again, for some libraries supporting only callbacks, a global promisify library may be needed to support async / await
Share this post
[social_warfare]
Buffers
Git and node.js

Get industry recognized certification – Contact us

keyboard_arrow_up