A-synchronous callback && promises

    SYNCHRONOUS  CODE

  • Synchronous basically means that you can only execute one thing at a time.
  • JS is a single-threaded means that can only execute one code at a time therefore the order is guaranteed.

                     Code executes step by step in order:

SYNCHRONOUS CODE: BLOCKING STATE AND SLOW PERFORMANCE

posts = loadpostsync();
// wait tills posts are fetched

dotheNextthing)(); // has to wait until posts are fetched


A-SYNCHRONOUS CODE: NON - BLOCKING STATE AND FAST PERFORMANCE

posts = loadpostsync(function() {
// wait till posts are fetched
// .. do something with posts
});


dotheNextthing)(); // doesnothave to wait until posts are fetched





         What if certain operations take a bit longer???


  • We simply have certain operations that cannot be finished immediately. It, not just the timer where we as a developer decides how long we take it. 

  • It also like other operations like HTTP request or getting User Location which simply means it will take a bit longer and typically you don't want to block your entire script until those operations finish because blocking script wouldn't mean that the next time won't execute immediately, it would mean another code cannot execute.

         THankfully browser has a solution for that ???




  •     So, if we have certain operations that would typically take a longer time to execute, We actually offload them to the browser.  What we do here is hand operation off to the browser by calling SetTimeOut ()   

  • We just tell the browser to set a timer but we then let the browser do that and therefore Our code can right away.                    

             "Now the browser is actually able to use the concept of multiple thread one for javascript and another for another task so that Our J.S code is not blocked" 

  • Then, the browser is responsible for managing the timer and the same for HTTP requests where we wait for our response and for getting the User location where we wait for the location. 
            

          How the browser will communicate back to JS???
  
  • The idea here is for example on set timeout() we pass a "callback function" as the first argument, that callback function is the function that the browser should call once the operation is done.so that, the browser can kind of step back into our code and execute there, Once it is done we can have our script continue to run  

  

Let us define the concept we just discussed in above section : 


    
"Asynchronous programming: is a form of parallel programming that allows a unit of work to run separately from the primary application thread. When the work is complete, it notifies the main thread (as well as whether the work was completed or failed). There are numerous benefits to using it, such as improved application performance and enhanced responsiveness."



Asynchronous code execution below:

 
 for eg: 

  • If I have an event listener and this is basically a similar thing . If we have ongoing listener, this would block the entire script because we have to wait for does the user now click, does the user now click that's not what we want to do, instead of when we add an event listener here, we also hand this task off to the browser which manages the listener behind the scenes.,so that our script execution can continue
     ---but we have the track User handeler func() here which we pass a second argument which effectively is a callback function,its the browser which can help you with the tasks that take longer and should not block your main thread--


<style>
#btn {
width: 100px;
height: 100px;
padding: 10px;
}
</style>
</head>

<body>
<button id="btn" type="button">click me</button>
<script>
const button = document.querySelector("#btn");

function trackUserHandeler() {
console.log('button clicked after callback func gets triggered')
}
button.addEventListener('click', trackUserHandeler);
console.log('will not wait to get button clicked')
console.log('callbackfunction is triggered');
</script>


</body>



output :

will not wait to get button clicked
function.html:28 callbackfunction is triggered
function.html:24 button clicked after callback func gets triggered

Let us consider another example:
 
<style>
#btn {
width: 100px;
height: 100px;
padding: 10px;
}
</style>
</head>

<body>
<button id="btn" type="button">click me</button>
<script>
const button = document.querySelector("#btn");

function trackUserHandeler() {
console.log('button clicked after callback func gets triggered')
}
button.addEventListener('click', trackUserHandeler)



let result = 0;
for (let i = 0; i < 100000000; i++) {
result += i;
}
console.log(result);
</script>

output:
4999999950000000
function.html:24 button clicked after callback Func gets triggered
 
"Click event are only processed once the loop process is done. Now we see single threading in action. we have this event listener, which we handeled this off to the browser and therefore this event is not blocking J.S and also this loop here is not handed off to the browser and no way of  this handed it off  "
  • This loop executes and J.S execution is blocked until this operation is done because you can only execute one operation at a time.          

  • THE, thing is if we click the event, whilst the loop is running,  the browser recognizes the click sure and it know it should call this function, So it does that but this function is J.S function and in J.S we are occupied with this for a loop.  
              "So this function is kind of memorized by j.s that it needs to execute that but it only does that once this operation is finished and that is what you really needs to understand.

                 That how JS works with Async code and synchronous code and what it actually does it use the concept called "event loop



        
        WHAT IS EVENT LOOP ???

  • Event loop helps us to deal with call back function in async code  scenario             

     


  • in the above code we define two functions then I set a timer. Once timer is done, we call Showalter() function which is the second function we define here and after the timer, we also called greet() here.

  • Now, this code executes along the stack which is part of the J.S engine as you learned will do certain things. Now, be aware that certain things will be offloaded to the browser, that the browser kind of gives us bridge where we can talk to certain browser API's from inside our J.S code 

  • When the code gets executes, the first function that is actually called is the built-in set timeout function. SetTimeOut does one important thing it reaches out to the browser because it actually is a browser API and made available in J.S and setup the ongoing timer there
   

  • This function is really done,it now doesn't block code execution Since the timer is still there that is managed by the browser.

  • The next thing that actually happen here not the show Alert function execution. keep in mind that it takes 5 milisec instead . Js does not wait for this and it instead moves into the next line greet method and now greet executes before the timer is completed. let us say we are done with the console.log() and therefore greet() also is done and the call stack is empty again.
 


  • whilist the execution time finishes , now we need to some way of telling that to our js code , so called JS engine to callback for the timer that holds our next function 

              For this the concept called "message queue is used"


  • In this Message Queue, the browser register any code that should be execute once we have time for it. In this case the timer,  show alertfunc() ,this callback is so to say is registered as TO_DO task.

         Now we need to get that message or this showalertfun()  in the call Stack

  • the eventloop just like a message Queue built into thr browser and most JS environment  For eg: node j.s have the concept for havins such event loop. It is really need to understand it is not part of js engine but part of host environment.

"the job of event loop in the end is just to synchronize the call stack in the engine without our waiting message" 


  • So in the end what event loop does is it runs basically all the timer and it always see is the stack empty ?? and do we have pending TO_DO's and if the stack is empty then event loop executes so to say it pushes any waiting msg or any to do fun() therefore into the call stack.
  • So the msg Queue is empty and now this function runs in our J.S code . So here Show alert runs. Once it is done, the call stack is empty again. 




"This is what browser does behind the scenes with the event loop and with our code and also with this callback function which we hand off to the browser API'S




    "This is the pattern typically used for async operations"



Playing with the navigator object and geolocation:

  • This is a user-built API which allows us to reach out to the browser to get user location with the help of GetCurrent Position.
          Get current Position is a method here which successfully takes three  potential parameters : 

Success callback: a function is executed if the position is executed successfully. 
Error callback: a function is executed if the position is not successfully executed. 
Option objects: that can be configured on how the option can be fetched.

<button id="btn" type="button">click me</button>
<script>
const button = document.querySelector("#btn");

function trackUserHandeler() {
navigator.geolocation.getCurrentPosition(posData => {
console.log(posData)
}, error => {
console.log(errorcallback)
});
console.log("geeting current position!!!");
}
button.addEventListener('click', trackUserHandeler);


             Output:  


 // for successful call back 
  1. GeolocationPosition {coords: GeolocationCoordinates, timestamp: 1590843711597}
    1. coordsGeolocationCoordinates
      1. accuracy53822
      2. altitudenull
      3. altitudeAccuracynull
      4. headingnull
      5. latitude27.7086208
      6. longitude85.3311488
      7. speednull
    2. timestamp1590843711597


"This is how callback work just as event listener, getCurrent position offloads this tack to the browser and when it has done it pushes it into the event loop to execute our code there."



         Mutiple callback and SetTimeOut
<button id="btn" type="button">click me</button>
<script>
const button = document.querySelector("#btn");

function trackUserHandeler() {
navigator.geolocation.getCurrentPosition(posData => {
setTimeout(() => { // callback
console.log(posData)
}, 2000);

}, error => {
console.log(errorcallback)
});
}


button.addEventListener('click', trackUserHandeler); // callback


let us add another call back function outside the trackUserhandelerfun()

<button id="btn" type="button">click me</button>
<script>
const button = document.querySelector("#btn");

function trackUserHandeler() {
navigator.geolocation.getCurrentPosition(posData => {
setTimeout(() => {
console.log(posData)
}, 2000);

}, error => {
console.log(errorcallback)
});
}
button.addEventListener('click', trackUserHandeler);



Live reload enabled.
function.html:34
timer done !!!
function.html:26
  1. GeolocationPosition {coords: GeolocationCoordinates, timestamp: 1590844852704}
    1. coordsGeolocationCoordinates
      1. accuracy53822
      2. altitudenull
      3. altitudeAccuracynull
      4. headingnull
      5. latitude27.7086208
      6. longitude85.3311488
      7. speednull
    2. timestamp1590844852704

 
"The reason for the browser to execute a callback function it always to take the route over message queue and event loop"



implications ???

  • what if we have such code where we have callback nested into each other like for eg: some callback with SetTimeOut with another callback again then maybe another function that yet uses another callback.
   
getCurrentPosition(()=>{
setTimeout(()=>{
doMoreSyncStuff(()=>{
// block of code .................... callback hell
});
}, 1000)
}, ...)


  • Then we enter something which has a nickname of "callback hell" because it gets cumbersome to read and maintain, it can be hard to track.


           "     Thankfully Javascript has solution for that  and that is the concept of Promises "


  • Promises, help to write a cleaner code because they look like below, we use a special keyword "then" and pass the callback to then. if we have another task depend upon the first task we simply add another "then block" instead of nesting into each other.
           "Therefore we have one level of nesting here instead of having multiples level"

                

someAsyncTask()
.then(() => {
return anothertask()
})
.then(() => {
return yetanothertask()
})
.then(...)


let us look it  practically :

  • Unfortunately,  SetTimeOut and getCurrentPosition have no Promise Version, so we cannot add "then" after that and use that.   More modern API's embrace Promises often only support Promises Syntax but these older functions like SetTimeOut() which has been forever used will still use Callbacks. Now we can actually wrap them into Promises support code if we want to execute and that how we will do here to understand Promises work internally.     

  • let start by adding new function here SetTimer and pass duration as an argument and here I will call set timeout () and pass the second argument as duration and first argument as a callback fund. 
   
  • now what we have to here or what can do we here is create new Promise, A Promises is end an object with functionality or with the idea of having such then method which you can call on it.

  • "A Promises is a Constructor function or the class built into the javascript and therefore we can use it like this. A Promise itself takes function as an argument, it takes the function that the Promise API will execute right away when the constructor is constructed."

  • The function takes two arguments which we pass to the constructor and each argument is actually itself a function: its a Resolve function () typically called resolve and reject function() typically called reject.

 "As I mentioned earlier function body must contain about what should happen here I actually want to set the timer so that I can access the duration." now the timer is set when the promises are created.





"Now in here, we have our async code then, this callback in the timer will only execute once the timer is done. So after creating a promise, not after a timer is done, I will return the promise here so that I can use it whenever I call setTimer, it returns as a return value of this function. Inside of the function body, we pass resolve function, and its a function that internally will mark this as a promise object . I will call the resolve once the timer is done and I can also pass the value there to resolve."

let takes a example:
let say, i created a function func1 that returns,
promises(is an object) that takes function as argument
and that function takes two other argument(resolve, reject)
itself is a function.




const func1 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const error = true;
if (!error) {
console.log('your promise has been resolved succesfully')
resolve(success);
} else {
console.log('your promise hasnot been resolved succesfully')
reject(error);
}
}, 4000)

})
}

// if i resolved this then execute this
func1()
// here sucess is equivalet to resolve
.then(success => {
console.log("harry thanks bro!!!")
})
// if reject then use .catch() to execute this
.catch(error => {
console.log('sorry harry bro!!!')
})









for eg: getting  user geolocation using promise chain concept:


<body>
<button id="btn" type="button">click me</button>
<script>
const setTimer = (duration) => {
const promise = new promise((resolve, reject) => {
setTimeout(() => {
resolve("i am done!")
}, duration)
})

}

const button = document.querySelector("#btn");

function trackUserHandeler() {
navigator.geolocation.getCurrentPosition(posData => {
setTimer(2000).then(data => {
console.log(data, posData)
})

}, error => {
console.log(errorcallback)
});
}

button.addEventListener('click', trackUserHandeler);
</script>



Chaining multiple promises:

let say i will new function getpostion() would be the name
and with argument opts and in there i will pass
navigator.geolocation.getcurrentposition and
now we need callback here , the good idea is to keep or
warp them with Promises as in above eg.

"now we have promisified version of getcurrentposition and therfore we can call down there in Trackuserhandeler()

<button id="btn" type="button">click me</button>
<script>
const getPosition = (opts) => {
const promise = new Promise((resolve, reject) => {
// since there is no promise version of navigator object
navigator.geolocation.getCurrentPosition(sucess => {
resolve(sucess)
}, error => {

}, opts);
})
return promise;
};

const setTimer = duration => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Done!');
}, duration);
});
return promise;
};

function trackUserHandler() {
// here posdata is a sucess Data
getPosition().then(posData => {
//the problem is: here i can set the
timer in this way but again it is nested here brings to
callback hell concepts here.//

// now this would be not so intelligent way of doing this.

// the better way is to take the advantage of concept called
chaining
lets see in below example:
setTimer(3000).then(data => {
console.log(posData);
})

})
setTimer(9000).then(() => {
console.log('Timer done!');
});
console.log('Getting position...');
}
const button = document.querySelector("#btn");
button.addEventListener('click', trackUserHandler)


concept of chaining introduces here:


const getPosition = (opts) => {
const promise = new Promise((resolve, reject) => {
// since there is no promise version of navigator object
navigator.geolocation.getCurrentPosition(sucess => {
resolve(sucess)
}, error => {

}, opts);
})
return promise;
};

const setTimer = duration => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Done!');
}, duration);
});
return promise;
};

function trackUserHandler() {
// here posdata is a sucess Data
let positionData;
Now, here actually we can return this inside of this function i passed to then
and what happens here is overall promises created here is now set back from
beingresolved to being pending.
now we can add new then() down there across multiple lines
getPosition().then(posData => {
positionData = posData;
return setTimer();
})
// then i here get the data of inner promise which is
in the case is the timer data i could log and this is where i introduced
.concept of multiple chainig
.then(data => {
console.log(data, positionData)
})
setTimer(9000).then(() => {
console.log('Timer done!');
});
console.log('Getting position...');
}
const button = document.querySelector("#btn");
button.addEventListener('click', trackUserHandler);


Promise Error handling:

We can also have things gone wrong, for eg when we are getting our position,
we have error callback in our promisified version what if we get that ?

we must passed into the promise chain here, now that is done with second argument
cofiguration function for the promise constructor gets reject argument.


  • We can call reject inside of arrow callback and for example pass our error object there
Now what rject will do it it will mark promise as failed !!!  now this is a failed state,
 now the promise is not pending but has a failed state there.


CASE I : 

"Error are not handeled with normal  then functions here", instead them takes 
second argument there"

  ----  the first argument is the callback function that should be executed , if promises 
resolves,the second argument is the potential error we might have ---                   
const getPosition = (opts) => {
const promise = new Promise((resolve, reject) => {
// since there is no promise version of navigator object
navigator.geolocation.getCurrentPosition(sucess => {
resolve(sucess)
}, error => {
reject(error)
}, opts);
})
return promise;
};

const setTimer = duration => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Done!');
}, duration);
});
return promise;
};

function trackUserHandler() {
// here posdata is a sucess Data
let positionData;
getPosition().then(posData => {
positionData = posData;
return setTimer();
},
// passing second argument for error state
)
// then i here get the data of inner promise which is in the case is
//the timer data i could log


// adding catch block error to handle the error state
.catch(error => {
console.log(error)
})


.then(data => {
console.log(data, positionData)
})
setTimer(9000).then(() => {
console.log('Timer done!');
});
console.log('Getting position...');
}
const button = document.querySelector("#btn");
button.addEventListener('click', trackUserHandler);

OUTPUT:
Live reload enabled. function.html:66 Getting position... function.html:56 GeolocationPositionError {code: 1, message: "User denied Geolocation"} function.html:61 undefined undefined function.html:64 Timer done!

" you see after reject the callback , you see still getting .then() block
executed there,it is skipping .then promise chain but after throwing error it is
again getting position" ???

" because this positioning of the Catch()
matter, so always keep catch(), method  at the end of chain"


<script>
const getPosition = (opts) => {
const promise = new Promise((resolve, reject) => {
// since there is no promise version of navigator object
navigator.geolocation.getCurrentPosition(sucess => {
resolve(sucess)
}, error => {
reject(error)
}, opts);
})
return promise;
};

const setTimer = duration => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Done!');
}, duration);
});
return promise;
};

function trackUserHandler() {
// here posdata is a sucess Data
let positionData;
getPosition().then(posData => {
positionData = posData;
return setTimer();
},
// passing second argument for error state
)
// then i here get the data of inner promise which is in
the case is the timer data i could log


.then(data => {
console.log(data, positionData)
})
// catch() at the end of chain skipping the .then() and
rejecting states
.catch(error => {
console.log(error)
})

setTimer(9000).then(() => {
console.log('Timer done!');
});
console.log('Getting position...');
}
const button = document.querySelector("#btn");
button.addEventListener('click', trackUserHandler);

</script>

Output:Live reload enabled.
function.html:65 Getting position... function.html:59 GeolocationPositionError {code: 1, message: "User denied Geolocation"} function.html:63 Timer done!


Async/await

  • There’s a special syntax to work with promises in a more comfortable fashion, called “async/await”. It’s surprisingly easy to understand and use.


  • Let’s start with the async keyword. It can be placed before a function, like this
The word “async” before a function means one simple thing: a function always
returns a promise. Other values are wrapped in a resolved promise automatically.
For instance, this function returns a resolved promise with the result of 1;
let’s test it:


async function f() {
return 1;
}

f().then(alert); // 1



EG:2
console.log('this is artcle about asnc await')
// use of async that return promises

async function func() {
console.log('inside func')
return func;
}
console.log('before calling func');
let res = func();
console.log('after calling func');
console.log(res);
console.log('end of the last line');

output:this is artcle about asnc await
index.html:22 before calling func index.html:19 inside func index.html:24 after calling func index.html:25 Promise {<resolved>: ƒ}__proto__: Promise[[PromiseStatus]]:
"resolved"[[PromiseValue]]: async ƒ func() index.html:26 end of the last line




IMPLEMENTATION OF FETCH API WITH

ASYNC AWAIT CONCEPTS:


  • Fetch(): the fetch API provides a fetch method defined on window object which you can use to peform request.
    : the method returns promises that you can use to retrieve the
response return of promises


console.log('this is artcle about asnc await')

" since async returns promises,i want two other promises
to get resolved that is implemented by await concept "

async function func() {
console.log('inside func')
const response = await fetch("https://api.github.com/users")
console.log('before response');
// now await for response of json
const users = await response.json();
return users;
return func;
}
console.log('before calling func');
let res = func();
console.log('after calling func');
console.log(res);
res.then(data => console.log(data));
console.log('end of the last line');

OUTPUT:
this is artcle about asnc await
index.html:27 before calling func
index.html:19 inside func
index.html:29 after calling func
index.html:30
index.html:32 end of the last line
index.html:21 before response
index.html:31
  1. (30) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
    1. length30







CONCLUSION:

      WE learned ASYNC CONCEPTS VS SYNC CONCEPTS:

  • From CALLBACK TO CALLBACK HELL.
  • CALLBACK HELL TO PROMISE(.then() and catch())
  • PROMISE TO ASYNC AWAIT 

Comments

Popular posts from this blog

JavaScript — Double Equals vs. Triple Equals - weird things on JavaScript

06_Understanding Execution Context and Execution Stack in Javascript

Creating and inserting element- part-2