DEV Community

Megan Lo
Megan Lo

Posted on • Updated on

Intro to Asynchronous JavaScript

Let's all be honest and address the elephant in the room. As a beginner developer, asynchronous programming is one of the hardest and confusing JavaScript concepts to grasp.

confusing bruce
A bit out of context, but can't we agree that the async concept can be as confusing as Bruce Banner didn't know there's an Ant-Man and a Spider-Man? 😅

I struggled to learn Promises and async and await in the past few months, and I have seen async and await in my interviews. I decided to write a series of Async JS to break this struggle and hopefully will help anyone who is trying to understand asynchronous programming in JavaScript.

There are 4 parts in this series:

  1. Intro to Asynchronous JS (this article)
  2. Promises
  3. More Promises
  4. async/await

Introduction

Before we start talking about Promises, async, await, we need to first understand why do we care about handling asynchronously?

(Disclaimer: I understand the importance of learning how async works in both client-side and server-side. But in this article, I will mainly focus on client-side JS rather than server-side. I would like to write more about server-side in the future.)

First off, we have to understand JavaScript is always synchronous and single-threaded. In other words, when one block of code is being executed, no other block of code will be executed.

synchronous demonstration

As you can see from above, the console printed the values according to the order.

JavaScript programs in web browser is typically event-driven. In other words, JavaScript is not going to do anything until the user tap or click on something. That's the client side. As for the JS-based server side, it usually waits for client requests to arrive over internet before doing anything.

We would use asynchronous JS in cases like fetching or accessing some kind of resource from a third party API.

Say you have a pretty large image on top of your website from a server, if we follow the JS synchronous style, the web browser has to completely finish loading the image before loading the rest of the content. For user experience, this behavior is not ideal, because you don't know how long the image will take to load.

If we use the fetch method to fetch the image from a server for the website, since fetch is asynchronous, when running the next line, it will throw an error as the response is not yet available (I PROMISE -- pun intended -- this will make more sense later on).

(You probably notice the images/GIFs in this article took a little bit time to load while the text is available -- a real example of asynchronous programming)

Asynchronous Programming with Callbacks

Before we dive into Promise (will be introduced in the next article), the most fundamental concept we have to understand is callbacks (passing another function in a function and will be invoked when some condition is met or some event is occured). This is also the old-fashioned way of handling asynchronous programming before the introduction of Promise in ES6. But some of these callbacks are still commonly seen without Promise.

Timers (setTimeOut())

Using the example above from the Introduction section, a quick refresher of what we want in the following order:

  1. ice cream
  2. boba tea
  3. iced coffee
  4. beach

What if I want boba tea after I go to beach, let's add setTimeOut() and get it 2 seconds (1000 milliseconds = 1 second) after I go to beach? Let's see how it looks like:

setTimeOut Demo

As you can see, "boba tea" appears ~2 sec after everything is printed on the console!

The first argument of setTimeOut() is a callback function and the second argument is a time interval measured in milliseconds.

There's another type of timer function called setInterval(). It is useful if you want a function to run repeatedly, but I will not cover in this article. Feel free to check this out here for more info about setInterval() on MDN.

Events

Speaking of events, you probably heard of addEventListener(). As mentioned in the intro, client-side JavaScript programs are almost universally event-driven. The web browser invokes these callback functions whenever a specified event occurs (as you may be familiar with hovering, clicking a mouse button, pressing a key on the keyboard). These callback functions are known as event listener and event handler.

addEventListener() is the method to perform these callback functions based on specified event in a specified content. The second parameter of addEventListener() method is an example of async callback.

Here is the example from W3Schools:

let btn = document.getElementById("myBtn");

// a callback function to be invoked when the user clicks on
// that button
btn.addEventListener("click", () => {
  document.getElementById("demo").innerHTML = "Hello World";
});
Enter fullscreen mode Exit fullscreen mode

Here's what happened, when a user clicks on a button that represent the HTML <button> element that has an ID myBtn, the text "Hello World" will show up.

addEventListener() Demo

The callback function is not immediately executed. Once any specified event occurs (in this case is "clicking"), the callback function will be performed asynchronously somewhere inside the HTML body.


✨ Pause for this iconic MCU GIF before we get to the final callbacks ✨
MCU: Last Stand

(I also needed to take a quick tea break here 😬)


Network Events/XMLHttpRequest

Last but not least, fetching data from a web server is another common source of asynchrony in JS programming (Like the example of fetching a large image I mentioned earlier in the intro section).

We would use an API object called XMLHttpRequest to interact with servers.

According to MDN,

XMLHttpRequest (XHR) objects are used to interact with servers. You can retrieve data from a URL without having to do a full page refresh. This enables a Web page to update just part of a page without disrupting what the user is doing. XMLHttpRequest is used heavily in AJAX programming.

Here's how it looks like (from MDN):

function loadData(url, type, callback) {
  let request = new XMLHttpRequest();
  request.open("GET", url);
  response.responseType = type;

  request.onload = function() {
   callback(request.response);
  };

  request.send();
}

function displayImg(pic) {
  let objectURL = URL.createObjectURL(pic);

  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image); 
}

loadData('starbucks.jpg', 'pics', displayImg);
Enter fullscreen mode Exit fullscreen mode

Quick breakdown:

We separate two functions:

  1. loadData to fetch the data from another server with XMLHttpRequest and displayImg to create an image to display the fetched data.
  2. We then take the displayImg as a callback function, as well as the URL and the content type. As the web browser loads the JS program, the XMLHttpRequest class plus the callback function would handle the server's response asynchronously and make HTTP request.

I know that's a lot to learn, but understanding the fundamental of callbacks helps understanding why Promises was introduced. In the next article, we will look into using Promises to simplify asynchronous programming.

See you in the next article!

Resources

🌟 Asynchronous JavaScript (MDN)
🌟 Eloquent JavaScript Chapter 11: Asynchronous Programming
🌟 JavaScript The Definitive Guide by David Flanagan (7th Edition) Chapter 13: Asynchronous JavaScript (Pg. 341 - 344) (Amazon)

Top comments (3)

Collapse
 
flaviojoni profile image
Flavio Joni Duarte da Silva

Thank you so much. Promises and callbacks are a really hard topic for me. I'll follow you and your articles to learn more about them.

Collapse
 
mehmehmehlol profile image
Megan Lo

Of course and I completely understand! I still had to refer to a lot of resources to help me write these articles!! The rest of the series will be published in the next 48 hours, thanks for the support and comment!

Collapse
 
dheeraj992 profile image
Dheeraj Mishra

Great Work Megan 😃 Loved Reading Your Content on Async Js