In my previous posts about JavaScript, I have already shown some ways of using little techniques to write a quality JavaScript Code. But Callbacks, Promises, and Async/Await are not a trick, they are a concept, and hence can’t be covered as a subtopic in an article. Hence, in this post, I shall try to cover these topics. We will begin with the concept of callbacks,  how to replace them with promise and then, in the end, we will see how to use async/await to easily deal with promises.  We will be using arrow syntax, as described in this article, to demonstrate the working in this post.  So let us begin.

CALLBACK:

As a JavaScript developer understanding the asynchronous nature of the language is very important. Asynchronous means that the thread does not wait for a function to return. It invokes a function and carries on executing other functions without waiting for the result. It is the responsibility of the invoked function to inform the thread when it is successfully (or unsuccessfully ) returns.  Now, the question arises, how does JavaScript achieve it.
The answer simply is CALLBACKS.

A callback is nothing but another function, which the main function calls when it returns or terminates. Along with the other params of the function you desire to call, you also pass in the callback function. This might be getting a bit confusing, so let us take an example. Let us write a function that returns a number after a random time interval.

 

function printNum(num){
  setTimeout(
    () => {
      console.log(num)
    }, 
    Math.floor(Math.random() * 100) + 1
  )
}

Let us try printing ‘1’, ‘2’ and ‘3’ in this particular order.

 

  let printer = () => {
   printNum(1);  //First call
   printNum(2);  //Second call
   printNum(3);  //Third call
};

printer();

This function printer executes 1 2 and 3 but in any random order. This order changes every time you execute the function.

This is because of the asynchronous nature of JavaScript.   After making the first call to printNum, the compiler does not wait for the response, it hits the second call and then the third call. Whichever returns first (with lesser timeout) is caught and printed to console. Now let us use callbacks to dictate the constraint of printing them in order.

 

 

function printNum(num, callback){
  setTimeout(
    () => {
      console.log(num)
      callback()
    }, 
    Math.floor(Math.random() * 100) + 1
  )
}

function printer(){
  printNum(1, () => {  //First
    printNum(2, () => { //Second
      printNum(3, () => {}) ///Third
    })
  })
}
printer();

Now let us analyze this carefully. The printNum function is now modified to take in a callback function, executed when printNum returns.  The  function call has changed too,  first function now includes a callback function (second call), which in turn has a callback function (third). So for the compiler there is only one function call printNum(1,callback) . When this function executes successfully the callback is executed. Callback here is a printNum(2, callback) , so after first executes, second is triggered , and second too has callback , printNum(3, callback) . Note that callback to third is just an empty function. So when the second is executed , third is called, and after successful execution of three, a blank function is called.

Thus we see that callbacks are chained, three will only be executed when second returns, and second only executes when first returns. Thus we force the order in the execution.
Our purpose is served, but the code becomes messy. Just imagine you have to force order on 100 such elements. You will have to chain 100 functions calls inside each other. A messy condition generally denoted as a Callback Hell.  Promises happen to save the day for us.

 

PROMISE:

So, what is a promise? A Promise is an object that may return a value in the near future, a resolved value in case function is successfully evaluated or an error in case it occurs.  A promise can be in one of these states: fulfilled, rejected, or pending.  A piece of code is always better than writing paragraphs worth explanation. So let us have a look.

 

function printNum(num){
  return new Promise((resolve, reject) => {
    setTimeout(
      () => {
       console.log(num)
       resolve()
      }, 
     Math.floor(Math.random() * 100) + 1
    )
  })
}

function printer(){
  printNum(1) //first
  .then(() => {
    return printNum(2) //second
  })
  .then(() => {
    return printNum(3) //third
  })
}
printer();

Let us see what is happening there. First the function printNum, it returns a promise object.  We have not yet considered any scenario for an error, thus we are not returning reject. Basically you return resolve when the function is successful, and it is caught by then block from the calling code.  Also what we see here is a promise chaining.  Third, is called from then of the second call which in turn is called by then of first call.  But even this code is a bit messy. Let us now see async/await.

 

Await is just syntactic sugar for a promise. It makes the asynchronous code looks similar to normal procedural code, making it very easy to understand. Let us now look at the code.  The printNum function does not change at all. What changes is the printer function?

 

  let printer = async () => {
   await printNum(1);  //First call
   await printNum(2);  //Second call
   await printNum(3);  //Third call
};

 

Just like that. The await keyword takes care of pausing the flow of thread to get the response and then move forward. Please note that await function only works inside an async function. The last thing we shall discuss here is what happens when printNum encounters an error. To demonstrate it let us change the printNum to only return the number if it is less than 10, else return an error.

function printNum(num){
  return new Promise((resolve, reject) => {
    setTimeout(
      () => {
       if (num <10 ) {
           console.log(num)
           resolve()
         } else {
          reject("Number is greater than 10");
       } }, 
     Math.floor(Math.random() * 100) + 1
    )
  })
}

let printer = async () => { 
    try { 
          await printNum(1); //First call 
          await printNum(2); //Second call 
          await printNum(3); //Third call 
        }
     catch(err) {
         console.log(err); 
     }
};
printer();

 

So what changes??  We use reject to return the error when the number is greater than 10. And in the printer function, we use try/catch block to check if printNum is rejected or resolved.
Hence we see how using async/await makes the code easier to read and write.

This is all about Callbacks, Promises, and Async/Await, we shall be discussing more JavaScript concepts in future articles.  Till that time keep experimenting with promises and try to get the best out of them.


0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *