Javascript is a single-threaded synchronous Language
What does the above state mean?
Via single-threaded we mean, only one statement is executed at a time in javascript
via Synchronous we mean, the program is executed line by line, one line at a time. Each time a function is called, the program execution waits until that function returns before continuing to the next line of code.
As we have read javascript performs only a single operation at a time in a synchronous manner. although we can manipulate this behavior of JS to make it behave asynchronously.
Before moving to promise and Async/await. let's first talk about what is async vs sync behavior and *why sometimes we need Javascript to behave asynchronously
Asynchronous vs Synchronous behavior
Imagine you go to a restaurant, a waiter comes to a table, takes your order, and gives it to the kitchen. Then they move on to another table, while the chef is preparing the meal So the same waiter can serve the many different tables. The table has to wait for the chef to cook one meal and when the meal is cooked waiter will serve food to that table. This is what asynchronous behaviors look like. Here the waiter is like a thread allocated to handle requests. So a single thread is used to handle multiple requests.
In contrast to asynchronous behaviors, In Synchronous Behaviors. you go to another restaurant and in this restaurant, a waiter is allocated to you. He takes your order and gives it to the kitchen. Now he is sitting in the kitchen waiting for the chef to prepare your meal and this time he is not doing anything else he is just waiting for he is not going to take any order from another table until your meal is ready. This is what we called synchronous behavior.
What are some of the cases in which you want javascript to behave asynchronously?
If you want to fetch some data from a server (which could take an unknown amount of time) it would be incredibly inefficient for your program to just completely freeze while it is waiting for that data to be fetched. So instead of doing that it’s common to just run the fetching task in the background and the remaining program so should run smoothly without any interruption and when the server responds we can perform the necessary operation on it depending upon if the request is resolved or rejected
There are three major ways to add asynchronously behaviour to the javascript
1.CallBacks
A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.
The functions in the Javascript are first-class functions i.e
we can pass a function as a parament to another function and call them later in the function.
we can also return the function from another function
Let me explain callbacks with the help of SetTimeOut
function a() {
console.log("a");
}
function b(callback) {
callback();
console.log("b");
}
function c() {
console.log("c");
}
b(a);
c();
As you can see the out of the above code would be
a
b
c
now let's make this code asynchronous using Set time out
function a() {
console.log("a");
}
function b(callback) {
setTimeout(() => {
callback();
}, 1000);
console.log("b");
}
function c() {
console.log("c");
}
Can you guess the output now?
b
c
a
It happens because the function "a" is executed asynchronously after 1 Second i.e in call stack 1st function that entered was b then it got executed and function a is sent to the browser to be called 1 second. then function "c" enters the call stack and is executed and after 1-second function "a" enters the call stack and gets executed at the end.
If you are using callbacks to handle asynchronous code sooner or later you will run into the problem of call back hell
What is callback hell? you may ask
According to google, Callback Hell is a slang term used to describe an unwieldy number of nested “if” statements or functions.
Modern javascript features like a "promise" and "async-await" help us Avoid callback hells
2.Promise
Javascript is designed to not wait for an async block of code to completely execute before running the synchronous code via promises we can control this flow and run a block of code only when the promise is returned
The promise is Javascript is the same as in real life. we make a promise for a future event and there could be only two outcomes of that promise either that promise will be full filled in the future or it won't be fulfilled
The same happens with javascript when we handle an async piece of code with javascript a promise returns a promise object with either resolved or rejected value and we can access these values using then and catch statement
A promise can only exist in three States
Pending: Initial State, before the Promise succeeds or fails.
Resolved: Completed Promise
Rejected: Failed Promise, throw an error
Let me explain to you with an example assume we made a data query to a server using the promise. The promise will be in the pending state initially and will be in that stage until we hear anything back from the server
If the server responds with the data the promise will go into a Resolved state
If the server responds with an error the promise will go into a Rejected state
that's all about the three states of promise
let's create our first Promise
Syntax for promise
const firstPromise = new Promise((resolve, reject) => {
// condition
//Promise will be either rejected or resolved on the basis of the condition
const condition = true;
if (condition === true) {
resolve("promise resolved");
} else {
reject("promise rejected");
}
});
we use the promise using then and catch statement if the promise is resolved it will enter the then statement and if the promise is rejected it will enter the catch statement
firstPromise
.then((successMsg) => {
console.log(successMsg);
})
.catch((errorMsg) => {
console.log(errorMsg);
});
When you are using promise in any complex app. you will chain your promise. what exactly is this chaining you may ask?.
Promise chaining is a syntax that allows you to chain together multiple asynchronous tasks in a specific order.
As you can see our promise is chained with .then, subsequent .then utilizes data from the previous one.
firstPromise
.then(msg1 => function1(msg1))
.then(msg2=>function2(msg2))
.then((msg3) => {
console.log("Success:" + msg3);
})
.catch((msg1) => {
console.log("Error:" + msg1);
})
Promises are a way to chain asynchronous operations cleanly without deeply nested callback hell
3.Async/Await
Await is basically syntactic sugar for Promises. It makes your asynchronous code look more like synchronous/procedural code, which is easier for humans to understand.
async function printMyAsync(){
await ayncfunctionOne("one")
await ayncfunctionTwo("two")
await ayncfunctionThree("three")
}
Async keyword work as a wrapper in telling the javascript that we are using async/await in the function and we are handling this function asynchronously await keyword can only we used inside the async wrapper,
Await keyword is used in an async function to ensure that all promises returned in the async function are synchronized, ie. they wait for each other. Await eliminates the use of callbacks in .then() and .catch()
Let me explain to you with the example of how async/await make our life easier
async function firstPromise(value){
try{
let msg1=await function1(value)
let msg2 =await function2(msg2)
console.log("success: "+msg2)
}
catch(error){
console.log(`Error ${error}`)
}
Async/Await help us write asynchronous code that looks and feel more like synchronous code
But remember Async/Await is just a wrapper around the promise and it makes promise easier to read but at its core is a promise only
Conclusion:
Now we know how to handle asynchronous behavior in Javascript using callbacks, promise and async/await. and pros and cons of each of them. I hope you can use them with better understanding in your projects now