Learning JavaScript? My Dating Analogy for CallBacks / Promises / Async & Awaits

Subscribe to my newsletter and never miss my upcoming articles

Javascript is a synchronous language, meaning it runs the code you write line by line until it gets to the end of the execution code. However, this isnt really practical, because imagine we have a Bot that sends messages to multiple people on instagram and we want our bot to deliver and display the message immediately it is sent.

In this case we wouldnt want the code that creates the message to stop our DM page from loading just because we are expecting more messages 🤷🏾‍♀️.

Ideally we would want the page to continue loading other parts of the page and when our data for the DM messages is ready, we can gracefully display it to the user. This is were the concept of asynchronous programming comes, it answers the question of

  • How can we handle multiple execution of code without one execution stopping the other from running?
  • And how can we do this using a synchronous language like JavaScript?

Many brilliant minds came together and ......

giphy.webp

Callbacks

This was the first approach to handling asynchronous code in Javascript. Lets review this method with some code.

// Slide into the DMS Bot 

const cheekyMessages = [
  {handle: "@mycodinghabits", message: "Hey crush!"}, 
  {handle: "@javaScriptDev", message: "I promise to always callback!"}, 
  {handle: "@100daysofcode", message: "My Javascript journey starts with you"}
]

function getDmMessages(){
  //The settimeout function here is pretending to be a request.
  // We have delayed the response by 1s
  setTimeout(() => {
    let messages = ''; 
    cheekyMessages.forEach((message, index) => {
      messages += `<li>Message from 
                    <span class="dms">${message.handle}</span> 
                    saying 
                    <span class="dms">${message.message}</span>`
    }); 
    document.body.innerHTML = messages;
  }, 1000)
}

// Lets create another function for out bot to create more dms
// This time we have decided to delay the execution by 2secs
function createDm(message){
  setTimeout(() =>{
    cheekyMessages.push(message)
  }, 2000)
}

createDm({ handle: "@mrniceguy", 
            message: "My love for you is like a Windows update.... Because it goes on forever and ever"})


getDmMessages()

Now the code above is ran synchronously which means line by line and after we finished calling the function getDmMessages() we get an output like this.

Untitled.png

But if you notice we didnt get the last DM from @mrniceguy and that is because the getDmMessages() function which displayes the DMs in our HTML has already finished execution before the createDM() function was called so therefore at that point the last DM could not be displayed. How can we fix this to show the last dm even if it takes longer to process. We do this by using callbacks.

// Lets create another function for our bot to create more dms
function createDm(message, callback){
  setTimeout(() =>{
    cheekyMessages.push(message)
    callback()
  }, 2000)
}

createDm({handle: "@mrniceguy", 
          message: "My love for you is like a Windows update.... Because it goes on forever and ever"}, 
          getDmMessages)

Now we have changed the createDm function to accept a callback function (This funtion can be named whatever we want), and now I would add the callback function just after @mrniceguys message was pushed into out cheekyMessages array. Lucky guy isnt he? Now our output looks like this.

Untitled 1.png

But here is the problem with using callbacks, multiple functions can be nested inside a function which leads to something called CALLBACK HELL. Example below

callMrNiceGuy(function(a){
    getToKnowMyrNiceGuy(a, function(b){
        goOnFirstDate(b, function(c){ 
            dateDidntGoWell(c, function(d){ 
                deleteMrNiceGuyMessage(d, function(e){ 
                    cheekyMessages.pop();
                });
            });
        });
    });
});

Now reading the code above is already giving me a headache —-🤕, let alone when we introduce other conditions and do more code execution for the variables we are passing into each function. This is where promises come to save the day!

giphy 1.webp

Alright terry! We heard you the first time!

How do we use promises to avoid Callback Hell? By wrapping our code in a promise and resolving the data we want to return or rejecting when something goes wrong. Lets take another look at our createDm function

// Using promises instead
function createDm(message){
  return new Promise((resolve, reject) => {
    lengthOfArray = cheekyMessages.length
    setTimeout(() =>{
    cheekyMessages.push(message)

    if(cheekyMessages.length > lengthOfArray){
      resolve();
    }
    else{
      reject("Dm was not delivered")
    }
  }, 2000)
  })

}

createDm({handle: "@JohnSmith", 
                    message: "Did your boyfriend tell you how pretty u were today?"})
                    .then(getDmMessages)
                    .catch((err) => console.log(err))

In this code example, we have wrapped our code in a Promise and use the resolve() function (Which is the getDmMessages() function when passed in) when our lastest cheeky message was successfully delivered and if for some reason the message was not delivered we have our catch block to give us a nice error message.

Notice how the .then() function is attached to the createDm function. It receives the function we would like to resolve. Using promises like this allows us to chain different functions to our original function and gracefully catch whatever errors are thrown.

But chaining multiple .then functions can still be very bulky and so JavaScript ES7 introduced a more elegant way of handling promises by introducing ASYNC/AWAIT. And to use this in our example we would encapsulate our code in slideInTheDMBot function.

async function slideInTheDMBot(){
    await createDm({
                      handle: "@TheBot", 
                      message: "Are you the projected spread of Coronavirus?
                    Because your curves are anything but flat"});

    getDmMessages()
}

slideInTheDMBot()

Look how much cleaner the code is! First we use the keyword ASYNC before we declare our function, then inside the function we add an AWAIT keyword which waits for our asynchronous function to complete before we call the function getDmMessages()

And there you have it. If you are wondering which you should use at any given time.

  • Ideally you should avoid using callbacks and use promises instead.
  • But you would seldomly be creating your own promises, insead you should be very familiar with using them, and finally
  • Async and Await is a cleaner way for handling promises and should be incorporated more.

Hope you enjoyed this read as much as I enjoyed writing it.

PS: Dont slide into the DMs like that, there is high chance you will be ignored! LOL

giphy_(1).webp

Till next time! Please share, comment and react to this post if you found it helpful!

Comments (2)

Dinys Monvoisin's photo

If there is one suggestion I can make is, in the callback section to put the createDM call before getDmMessages, so that it clear to the reader that it is not a wrong order of execution which is causing @mrniceguys message not to be print. Other than that, thank you for refreshing my mind about the different ways to handle asynchoronous code.

Omotola Shogunle's photo

Thank you Dinys Monvoisin, that was actually a great point you made. I have made the changes.

And you are welcome, writing the article was a refresher for me as well.