Callbacks, Promises, async, await, try, catch - Heart of Javascript

Callbacks, Promises, async, await, try, catch - Heart❤️ of JavaScript

The whole purpose boils down to having Synchronous or Non-Blocking code and let's understand it now, 👇


Most Important thing - Why? - Why we at all need Callbacks, Promises, Async, Await ?
Because JavaScript is always in hurry to go through line by line fastly (asynchronous execution). And it differentiate with the code, will talk about this later also. It instantly executes and completes one part of code and doesn't another by skipping on it.

Firstly, let's see which kind of code gets executed instantly with no need of extra time ⏩. (doesn't asks for "time-please").

Work                                                                       Identifiers

Initialization and Updation of Variables            let myUser= "Binod", let users = [], users.push(myUser);

Loops                                                                      for(let user of users){}, users.map((user)=>{});

 

Secondly, let's see which kind of code need extra time to complete and gets skipped. (asks for "time-please"🐌⌛).

Work                                                                       Identifiers

Calling an API                                                        fetch, axios etc..

Delay                                                                       delay(2000);

Reading file                                                            fs.readFile("myFile","utf-8", callbackFunction)

Using setTimout                                                    setTimout(Workfunction , 2000); 

Calling an await function                                     await anyFunction();

CURD in Database                                                 await db.user.find({});

etc...  such kind of code needs time to finish.

Now, We'll have to understand the different execution style of both different type of code above.

Firstly, the first kind of code that doesn't asks for "time-please", The JavaScript decides let's completely execute this right away and move on.
But,
On the other hand, the second kind of code which do ask for "time-please", naturally the JavaScript doesn't likes to wait, so it decides to come back to this code later after it finishes, and so for now let's move on. This behavior is called Asynchronous execution of code.

Problem Statement (Problem with the above Asynchronous behavior) :

The problem arises in a case where the next underlying code depends of the complete execution of it's above written code.

For Example:-

CODE

let myObj;

// this readFile function asks for "time-please"
myObj = fs.readFile("file.txt","utf-6", (err, fileData)=>{
        console.log(
"this is the file data: ", fileData);
});

console.log("following next line, I want to execute this line after reading contents of file.txt");

console.log("End"); 

 OUTPUT

following next line, I want to execute this line after reading contents of file.txt
End

this is the file data: ....

So, the problem here is that the lines are not executed complete in sequence, the asynchronous JS didn't waited to read the file and finish up, but it skipped and jumped to the next following line. Till now we understood the problem statement completely.


Now it's time for the solutions to the above stated problem statement:

1. Callbacks nesting

Callbacks are very basic, easy but weird solution to our problem statement, let's directly see the solution through callbacks:

CODE

let myObj;

// this readFile function asks for "time-please"
myObj = fs.readFile("file.txt","utf-6", (err, fileData)=>{
        console.log(
"this is the file data: ", fileData);
       
console.log("following next line, I want to execute this line after reading contents of file.txt");
});

console.log("End");

 OUTPUT

End
this is the file data: ....
following next line, I want to execute this line after reading contents of file.txt

Notice that we somewhat made the reading of the file and printing the next following file wanted synchronous to each other by writing them inside callback sequencially.

The problem with this is callback hell (many callback nesting together): 

Till yet we didn't introduced the concept of promise.

Now, it's time for some promises :p

2. Promises

To save us from the above shown callback hell and to overall make our life simpler by introducing synchronicity in our code we use this very easy concept called Promises.

What is a promise?
Basically a promise is an object (seems easy right? it actually is !).

A JavaScript Promise object can be:

  • Pending
  • Fulfilled
  • Rejected

The Promise object supports two properties: state and result.

While a Promise object is "pending" (working), the result is undefined.

When a Promise object is "fulfilled", the result is a value.

When a Promise object is "rejected", the result is an error object.

myPromise.state               myPromise.result
"pending"    ->        undefined
"fulfilled"    ->        a result value
"rejected"        ->        an error object 

NOTE:- We can store the promise from any promise returning function directly into any object.

In our Analogy, promise is a proper format slip of "time-please" request. :p

We use promiseObject.then to get the data in both fulfilled state and rejected state. We use promiseObject.catch to get the error(kind of a data only) in rejected state of the any promise stored in promiseObject.

NOTE: We can use second argument of .then function to get the error.🤯 (I've discussed about this in a different blog in detail).

Let's see the syntax:

CODE

let promiseObject = fs.readFile("file.txt", "utf-8"); // this readFile function asks for "time-please"

console.log(promiseObject); // this will give us undefined

promiseObject.then((data)=>{ // this is the syntax of reading a fulfilled promise
        console.log("this is the file data: ", data);
       
console.log("following next line, I want to execute this line after reading contents of file.txt");
});

console.log("End"); 

OUTPUT

undefined
End
this is the file data: ...
following next line, I want to execute this line after reading contents of file.txt


3. Await

General Human behavior - If someone promised us something and she's taking some time to get back to you. So generally what you'll do ? Yes, you'll wait endlessly for her to get back to you and hope for the best only. :p

Similarly,

To do so in JS, we use await. Simple !
Finally this gives us the power to make JS completely synchronous.

NOTE:- await can only be used inside an async function (We'll learn about async in point no. 5).
NOTE:- await only receives the data on a fulfilled state of a promise. (as also seen in our Analogy- hoping for the best only). 🤯
p.s.: We'll see how to handle the rejected state of a promise just after the following code.

CODE

let promiseObject = await fs.readFile("file.txt", "utf-8"); // this readFile function asks for "time-please" but now using await we are waiting for it right here to finish up before moving on further.

console.log("this is the file data: ",promiseObject);

console.log("following next line, I want to execute this line after reading contents of file.txt");

console.log("End");

OUTPUT (one of the best yet)

this is the file data: ....
following next line, I want to execute this line after reading contents of file.txt
End

Now, you must be guessing how to handle the case if the promise is rejected !

4. try and catch

Let's see, while using await we handle the rejected state of a promise using try, catch statements.

CODE (notice how I just copied the same above code inside a try block)

try{

        let promiseObject = await fs.readFile("file.txt", "utf-8"); // this readFile function asks for "time-please" but now using await we are waiting for it right here to finish up before moving on further.

        console.log("this is the file data: ",promiseObject);

        console.log("following next line, I want to execute this line after reading contents of file.txt");

        console.log("End");

}
catch(err){ // this is where the error comes from the rejected promise.

        console.log(err);

}

Pro Tip : catch(error) {...} statement catches only awaited rejected promises in try {...} statement.

5. Async

Simply put, async makes a function return a Promise (Remember our Analogy of a Promise ? - a proper format slip of "time-please" request).

So, I we make any function async it will return a promise.

CODE

const myFunction = async() => {
        return "Hello";
}

console.log(myFunction());  // this prints a promise object

Bonus - let's see an alternative method to make a function return a promise.

6. Bonus

Another way of making a promise returning function other than async :

const myFunction = async() =>{
        return new Promise((resolve, reject)=>{
                resolve("Hello"); // fulfills the promise
        })
}

That's all, I hope This Blog strengthened and set your concepts in JS once in for all. ✌️

Happy Coding :)
"Write to Learn"

Comments

Popular posts from this blog

Mastering Retention - The power of Spaced repetition and Deliberate Practice

Free & Easy Designing tools for a Software Developer

My Advice to my Juniors, My Biggest College mistakes - Raw