Asynchronous Javascript
Js code execution
JavaScript is single-threaded and synchronous by default. Asynchronous functionality is achieved using callbacks, promises, and async functions. Asynchronous code runs simultaneously, meaning it doesn't wait for anything to complete before moving on. When executed, JavaScript code enters the call stack.
There are two phases:
Memory Allocation: allocates the memory to variables and functions.
Execution Phase: executes the functions and allocates the values.
//code let a = 10 function example(){ console.log("hello") } example() //memory allocation a = undefined example = {....}//function code //execution phase a = 10 "hello"
There are two main queues:
Microtask Queue: Contains tasks created by promises and async functions. It has higher priority than the callback queue.
Callback Queue: Contains callback functions like setTimeout and callback
functions.
Synchronous and Asynchronous
Synchronous means executes line by line that code which enters first into to call stack it will execute first. but, if any code takes a long time. It will wait for the execution of that block of code so, it may lead to the blocking of the code.
console.log("start")
function sync(){
console.log("Iam a function")
}
sync();
console.log("end")
Asynchronous code in JavaScript lets tasks happen at the same time, without waiting for one to finish before starting the next. Using promises and callbacks, JavaScript manages asynchronous tasks smoothly.
function async(){ setTimeout(()=>{ console.log("after 2 sec") },2000) } async function example(){ const ans = await fetch('https://api.github.com/users/KalyankarPooja') const finalAnswer = await ans.json() console.log(finalAnswer) } async() example() //In this example function execute first because async has first prority then setTimeout
Call Stack Queue
Call Stack Queue is used to execute the js code, it follows the two phases
Memory Allocation phase: when the code starts running, it first sets up memory space for variables and functions. Variables are like empty boxes created to store values, but initially, they don't have any specific value. Functions are stored as blocks of code in memory, ready to be used later on."
Execution phase: In this phase, the JavaScript engine starts executing the code. Functions are pushed onto the call stack in the order they are encountered. The function at the top of the stack is executed first. This process follows the Last In, First Out (LIFO) principle, meaning the most recently added function is the first to be executed.
let number = 10;
function first(){
second()
console.log("I will execute after second")
}
first()
function second(){
console.log("I will execute first only")
}
console.log(number)
Event Loop
- Event Loop is used to check continuously the call stack queue whether it is empty or not and it also checks the microtask queue and callback queue if there is any task to execute.
Inversion of control
Inversion of control occurs when we give control of our callback function to another function and we trust that it will call our callback function whenever it needs but we are not sure about it. That's why by using callback it will occur.
function inversionOfControl(variable,callback){ console.log(variable) callback(variable) //here giving the callback function control to inversionControl funnction } inversionOfControl(3,(variable)=>{ console.log("I will execute after first",variable) });
Callback Hell
Callback hell occurs when we use the nested callback functions.
ways to prevent callback hell by using promises and async-await.
function callback1(callback){
setTimeout(()=>{
console.log("first")
callback()
},2000)
}
function callback2(callback){
setTimeout(()=>{
console.log("second")
callback()
},2000)
}
function callback3(callback){
setTimeout(()=>{
console.log("third")
callback()
},2000)
}
callback1(()=>{
callback2(()=>{
callback3(()=>{
console.log("call back hell")
})
})
})
//by using promises
callback1()
.then((data)=>{
console.log(data)
return callback2()
})
.then((data)=>{
console.log(data)
return callback3()
})
.then((data)=>{
console.log(data)
})
.catch((error)=>{
console.error(error)
})
function callback1(){
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve('first')
},2000)
})
}
function callback2(){
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve('second')
},2000)
})
}
function callback3(){
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve('third')
},2000)
})
}
Promise
Promises are the object that represents the eventual completion of asynchronous functions. it helps to perform asynchronous operations more efficiently way.
promise object contains two:
State
Pending: Initial state, neither fulfilled nor rejected.
Fulfilled or Resolved: The operation was completed successfully.
Rejected: The operation failed.
Result
Resolved: Contains the result of the operations.
Rejected: Contains reason for rejection.
Promise APIS
Promise.All: it takes an array of promises and if all promises resolve then gives the result as an array of values and if any promise is rejected, it gives that error as output.
Promise. all settled: it takes an array of promises and gives results as all settled promises.
Promise. race: it gives the result of a first settled promise.
Promise. any: it gives the result of the first fulfilled promise, if all promises rejected then it gives aggregation of error.
//Promise Creation
function promiseCreation(){
new Promise(function(resolve,reject){
setTimeout(()=>{
resolve("promise created")
},2000)
})
.then((data)=>{
console.log(data)
})
.catch((error)=>{
console.error(error)
})
}
promiseCreation()
Web API
Web APIs provide the resources and functionality to access and interact with the network throw a browser.
fetch: it used fetch the resources from the internet, and it always returns results as promised.
console: it is used for logging messages and debugging information to the console.
setTimeout: it is used to delay the execution of code for a given milliseconds
DOM APIs: Tools for changing what a webpage looks like and how it behaves.
async function practtice() {
try {
let url = 'https://api.github.com/users/KalyankarPooja'
let ans = await fetch(url)
let conJson = await ans.json()
setTimeout(() => {
console.log(conJson)
}, 2000);
} catch (error) {
console.error(error);
}
}
practtice()
Micro Task Queue
A microtask queue is used for quick tasks so we put those tasks in the microtask queue like promises.
console.log('Start');
setTimeout(() => {
console.log('setTimeout callback');
}, 0);
Promise.resolve().then(() => {
console.log('Promise resolved');
});
console.log('End');
Error Handling in Promises
Error handling in promises by using the .catch block.
function promise(){
new Promise(function(resolve,reject){
reject("I am Error")
})
.then((data)=>{
console.log(data)
})
.catch((error)=>{
console.error(error)
})
}
promise()
Async...Await
the async keyword is used to create an async function by using the async function, it makes pauses and resumes the execution. and returns the promises.
await is used in the async function and it waits for the promise object to settle.
let fs = require('fs').promises
let p1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("first")
}, 2000)
})
let p2 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("second")
}, 5000)
})
async function example() {
let ans = await p2;
console.log(ans)
let ans1 = await p1;
console.log(ans1)
}
example()
Difference between Async...Await and Promises
Async...Await allows to writing of asynchronous code using the async and await keywords. but in promises writing of asynchronous code by using .then and .catch keywords.
In async...await error handling can be done by using the try and catch block and in promises only .catch method is used for error handling.
async...await code is more readable as compared to promises code.
Best ways to avoid nested promises
Chaining promises
let fs = require('fs').promises
let p1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("first")
}, 2000)
})
function useValue(data){
return new Promise(function (resolve, reject) {
setTimeout(() => {
resolve(data+"second")
}, 5000)
})
}
function example() {
let ans = p1
.then((data)=>{
return data;
})
.then((data)=>{
return useValue(data)
})
.then((data)=>{
console.log(data)
})
.catch((error)=>{
console.error(error)
})
}
example()
By using async...await
let fs = require('fs').promises let p1 = new Promise(function (resolve, reject) { setTimeout(() => { resolve("first") }, 2000) }) function useValue(data){ return new Promise(function (resolve, reject) { setTimeout(() => { resolve(data+"second") }, 5000) }) } async function example() { try{ let ans =await p1 let final= await useValue(ans) console.log(final) }catch{ console.error(error) } }