Asynchronous Promises with JavaScript

JavaScript generally executes in a synchronous fashion. Meaning the code runs from top to bottom, one line or block at a time, and functions rely on completion of other functions to finish executing before the the program can run them. For example:

Task A runs in the program, but Task B cannot start execution before Task A is finished. Same circumstances occur with Task C. Task B must be completed before Task C can be executed.

This is the basis of synchronous program execution. JavaScript also is a single threaded language. Meaning that each thread is a single process used to complete certain tasks. Each thread can only perform a single task at a time.

With today’s computers, which have multiple core processors some languages can perform tasks with multiple threads using different processors. JavaScript can use Web Workers, which allow for separate tasks to be completed in the background separate from the main thread. The problem with Worker threads is that they cannot access the DOM (Document Object Model). This means that Web Workers cannot do anything to manipulate the UI.

In order to solve this issue, browsers allow developers to use asynchronous operations. We can run these asynchronous operations using something called promises.

With asynchronous promises, it’s important to understand what asynchronous operations actually mean. Asynchronous operations basically mean that programs can run operations in the background, and continue to execute the rest of the program in the meantime. Here’s a quick visual example:

Async operation occurs elsewhere without blocking the main thread of execution.

You can think of it as the browser saying something like, “I promise to return a result as soon I finish completing the task.” Promises can only have two outcomes, successful completion or failure.

In this case let’s take a look at using fetch() as our asynchronous operation:

Example of fetch and promise using .then() block and .catch() block for error handling.

In this example (starring my newly adopted puppy Gunnar :)), we use fetch to access the image file. After fetching, we use .then() with a callback function to do a couple things:

  1. create an img element and set the src attribute to the response’s url key. Since the .then() block has access to the promise object we can access the values within that object using dot notation and the correlating keys. Using console.log(res) allows us to examine the promise object.
  2. Then we set the class name again using dot notation. This way we can style the size of the image in the styles.css files imported at the top of this file.
  3. Finally we can append the newly created img element to the div element that we set to the container variable previous to fetching.

In this case, the fetch returned a successful promise completion. Sometimes however, fetch will return a promise with errors because it failed. In these circumstances, we can use .catch() blocks to catch the error.

We can then do what we want with the error responses. Here is an example of error handling:

Example of error handling with .catch() block

In the above example, .catch((error) => {console.log('There has been a problem with your fetch operation: ' + error.message)} is where we are handling errors. We console log a string and error.message to display the exact error that results from the failed fetch.

In conclusion, async operations within our code allow us to perform operations that run in the background. While the rest of our program is able to be executed. Using promises, we can run operations in the background using the promise object that is returned from .fetch() blocks. This allows developers to have more flexible and user friendly code.

Software Engineer | Full Stack Developer | Soccer Fanatic