Asynchronous Javascript
JS Code Execution
Javascript is a synchronous and single-threaded language. When js engine reads the script then it creates an environment where all the code executes called Execution context.
Two types of execution context:
Global execution context: It represents the global scope of js.
Function execution context: It represents function's local scope
Two Phases
Creation Phase:
Creates global object and sets up memory to store the variables and functions, variables value stored as undefined and function with references
Execution Phase :
Now it reads the whole code line by line and evaluates it, then the value of undefined changes to the allocated value after completion of execution it is destroyed.
Example:
//craetion phase
n=undefined
m=undefined
add{..}
addition=undefined
//execution phase
n=5
m=6
add{...}
addition = 11
Differences between Synchronous & Asynchronous
Synchronous: It executes in a sequence, after completion of the previous instruction only the next instruction starts. The drawback of synchronous was sometimes the important instructions may get blocked because of previous instructions which take a long time.
console.log("Hi")
console.log("bye")
Asynchronous: Asynchronous came into the picture to prevent the drawbacks of synchronous. It executes the next instruction immediately, it does not block the flow of previous ones. In the below code, there is a delay of the first instruction for 2 sec as it is asynchronous it will not block there and the first prints the hello and after 2 sec hi is printed.
setTimeout(()=>{
console.log("hi")
},2*1000)
console.log("hello")
Callstack queue
A Callstack queue is used to track the functions in a program. Functioning of it is when the script calls the function it is stored in the callstack after that if the function is called present in the callstack it runs and completes that function and then takes it out from the callstack. As in the name present it follows last in first out.
function first(){
console.log("first")
}
function second(){
first()
console.log("second")
}
second()
Event loop
It handles asynchronous operations like promises etc.
Components of the event loop
Callback queue: In callback queue callbacks are present like setTimeout(),setInterval() etc
Microtask queue: In microtask queue asynchronous functions present like promises etc.
The event loop will help to push the tasks in callback queue and microtask queue one by one into call stack.
Inversion of control and Problems causes
Inversion of control means having control in one part of a code, when we give another person this code for the next step, the control goes to him.
The problem with this was if the control went to another person then he may change before the written code there may arise problems.
Ways to Avoid callback hell
By using promises or by using async await we can avoid callback hell
//this is callback hell
main(function(result) {
sub1(result1, function(final) {
sub2(final, function(finalResult) {
console.log(finalResult);
});
});
});
main()
//using promises how to resolve callback hell
main()
.then(result => sub1(result))
.then(result1 => sub2(final))
.then(final => {
console.log(finalResult);
})
.catch(error => {
console.error(error);
});
Promise
Promise is an object that represents the completion or failure of asynchronous operations.
It has three states
Pending - Execution of promise is ongoing
Fulfilled-Completed execution of promise and result is success
Reject-Completed execution of promise but it gets rejected
//using of promises const fspromises = require('node:fs').promises function sample(){ fspromises.readFile('./data.js','utf-8') .then((data)=>{ return data }) .then((data)=>{ console.log(data.toUpperCase()) }) .catch((error)=>{ console.log(error) }) } sample()
The promise is solving the callback hell and inversion of control problems.
Create Promise
The below code illustrates how to create promise.
const newone = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("Hello")
},2000)
})
newone
.then((data)=>{
console.log(data)})
.catch((err)=>console.log(error))
Promise API's
promise.all() - After solving all promises it will return values of array as output.If one of them is rejected then error of that promise is given as output.
promise.allsettled()-After solving all promises it will return values of array,if any one is rejected then also it will values of array but error as output for that reject promise.
promise.race()-It gives the value of first completion promise.If it is error then it will return error.
promise.any()- It gives the first completion promise,if there is any error then it will return the next success one.
Web browser APIs
Web browser APIs are sets tools web browsers provide to allow developers to interact with and manipulate the browser environment and the user’s device.
There are many Web Browser APIs, Some of them are
SetTimeout: To set time, after how much time function should be called
DOM API: To change structure or style we use this
fetch: It enables communication with servers.
console: To log information on the browser
Microtask Queue
Microtask queue is an essential component of the event loop, these are used to execute the next task by immediate completion of the present stack. In the microtask queue promises are present and it performs async operations.
console.log("hello")
promise.resolve()
.then(()=>{console.log("promises are utilized by microtask queue")})
console.log("bye")
Handling errors while using promises
Through the catch() method we can handle promises. Anywhere there is an error it will catch and handle it. Catch only handles errors for promises that are present before it.
function dosomething(){
return new Promise((resolve,reject)=>{
const data = "promise data"
setTimeout(()=>{
reject(new Error("Error occured"))
},2000)
})
}
dosomething()
.then(data=>{
console.log(data)
})
.catch((error)=>{
console.log(error.message)
})
Async. Await
Async await is an asynchronous function alternative for promise that will look like synchronous code that means it provides clean and readable format of asynchronous code.
async-In this we have to write async keyword before function, it returns the promise and allow to write await keyword.
await-This keyword is used to wait till the promise completed after completing it will console.
const p1 = new Promise((resolve)=>{
setTimeout(()=>{
resolve("hi")
},5000)
})
const p2 = new Promise((resolve)=>{
setTimeout(()=>{
resolve("hello")
},2000)
})
async function handling(){
console.log("first")
const promise1 = await p1
console.log(promise1)
const promise2 = await p2
console.log(promise2)
}
handling()
Different between promise & async..await
Promise involves chaining of then and catch whereas async await uses keywords of async and await and it looks like synchronous code.
In promises we use catch method for error handling and in async await uses try catch.Try catch is easy to handle errors.
Async await is more readable than promises.
When concurrent operations present it is better to use promises and in remaining cases better to use async await.
Best ways to avoid nested promises
1.Chaining promises
promisesChaining()
.then((data)=>{filter(data)})
.then((filtered)=>{update(filtered)})
.then(()=>{console.log("updated data successfully")})
.catch(()=>console.log("error"))
2.Using promise.all for concurrent actions
function data1(){
return new Promise((resolve,reject)=>{
setTimeout(()=>resolve("Hi")
},1000)
}
function data2(){
return new Promise((resolve,reject)=>{
setTimeout(()=>resolve("Hello")
},2000)
}
promise.all[data1(),data2()]
.then((result)=>{console.log(result)})
.catch((err)=>{console.log(error)})
3.async await
function filterData(){
console.log("filter")
}
function updateData(data){
console.log("updated")
}
async function handling(){
const data = await filterData()
await updateData(data)
}
handling()
These are some ways to avoid from nested promises,because of nested, it is tough to read and tough to solve if any errors occured.