
Although, Javascript is single-threaded , web APIs make us feel like everything is happening asynchronously. Current state of asynchronous programming has not been developed overnight. It has been there for quite a long time and has evolved over time. Below are the ways used to work asynchronously till date
- Callbacks
- Promises
- Async Await
Callbacks
A function is a first-class citizen in Javascript. It can be passed as variable to another function and can be invoked. It is the mechanism of callback. A function is passed to another function as an argument and is invoked from it. It has no return statements. Example:
function funcA(anotherFunc)
{
// do something
anotherFunc();
}
In the above example, anotherFunc is passed as an argument to funcA and is invoked inside it .
Drawbacks:
- This way of invoking a function can quickly lead to callback hell (when multiple functions are executed one after another, callbacks are nested)
const fs = require('fs')
const callbackHell = () => {
return fs.readFile(filePath, (err, res)=> {
if(res) {
firstCallback(args, (err, res1) => {
if(res1) {
secondCallback(args, (err, res2) => {
if(res2) {
thirdCallback(args, (err, res3) => {
// and so on...
}
}
}
}
}
}
})
}
- Error handling gets messy.
Promises
Promises are there for quite a long time now. A promise ascertains the completion/failure of task at some time. Promises can be in one of three states
- Pending : initial state
- Resolved : denotes that task completed successfully
- Rejected : denotes that task failed
A promise can be created as
let myPromise = new Promise((resolve,reject)=>{
setTimeout(()=>{
let isSuccess = DoSomeWork();
if(isSuccess){
// this will be dealt in then block
resolve("Promise completed successfully");
}
else{
//this will be dealt in catch block
reject("Task failed");
}
},3000)
})
We can work with the above promise like
myPromise().then(res=> {
// if promise is resolved, control flows to this block
console.log(res);
}).catch(ex=>{
//if promise is rejected , control flows to this block
console.log(ex);
})
In the above example,
if promise is resolved then control flows to then block and Promise completed successfully is logged if promise is rejected, control flows to catch block and Task failed is logged
Promises can be chained one after another removing the drawbacks of callback hell.
myPromise()
.then(res=> DoSomething(res))
.then(res=>DoAnotherThing(res));
Async Await
As promises, it deals with completion/failure of task at later time but in much nicer and cleaner way. If you are familiar with asynchronous programming in C#, this syntax looks familiar.
Example
try
{
let data= await myPromise();
console.log(data);
}catch(ex){
console.log(ex);
}
consider , myPromise as promise mentioned above,
if promise rejects, control flows to catch block if promise resolves, response is stored in data variable and statements below it are executed
This method removes the drawbacks of callback hell and chaining then blocks and is most cleaner compared to above methods.
Thank you for reading !!!