Site icon Tutorial

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:

Cons:

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:

Cons:

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:

Cons:

Exit mobile version