Advertisement
  1. Code
  2. Node.js
Code

JavaScript Callbacks, Promises, and Async Functions: Part 1

by
Difficulty:AdvancedLength:MediumLanguages:
This post is part of a series called JavaScript Callbacks, Promises and Async Functions.
JavaScript Callbacks, Promises, and Async Functions: Part 2

Introduction

There is a lot of talk about asynchronous programming, but what exactly is the big deal? The big deal is that we want our code to be non-blocking. 

Tasks that can block our application include making HTTP requests, querying a database, or opening up a file. Some languages, like Java, deal with this by creating multiple threads. However, JavaScript has only one thread, so we need to design our programs so that no task is blocking the flow. 

Asynchronous programming solves this problem. It lets us execute tasks later so that we don’t hold up the entire program waiting for tasks to complete. It also helps when we want to ensure tasks execute sequentially. 

In part one of this tutorial, we will learn the concepts behind synchronous and asynchronous code and see how we can use callback functions to solve problems with asynchrony.

Contents

  • Threads
  • Synchronous vs. Asynchronous
  • Callback Functions
  • Summary
  • Resources

Threads

I want you to remember the last time you went shopping at the grocery store. There were probably multiple cash registers open to check out customers. This helps the store process more transactions in the same amount of time. This is an example of concurrency. 

To put it simply, concurrency is doing more than one task at the same time. Your operating system is concurrent because it runs multiple processes simultaneously. A process is an execution environment or an instance of a running application. For example, your browser, text editor, and antivirus software are all processes on your computer that are running concurrently.

Applications can also be concurrent. This is achieved with threads. I won’t get too deep because this goes beyond the scope of this article. If you would like an in-depth explanation of how JavaScript works under the hood, I recommend watching this video

A thread is a unit within a process that is executing code. In our store example, each checkout line would be a thread. If we have only one checkout line in the store, that would change how we processed customers. 

Have you ever been in line and had something hold up your transaction? Maybe you needed a price check, or had to see a manager. When I am at the post office trying to ship a package and I do not have my labels filled out, the cashier asks me to step aside while they continue checking out other customers. When I am ready, I go back to the front of the line to be checked out. 

This is similar to how asynchronous programming works. The cashier could have waited for me. Sometimes they do. But it is a better experience to not hold the line up and check out the other customers. The point is customers do not have to be checked out in the order that they are in line. Likewise, code does not have to be executed in the order in which we write it. 

Synchronous vs. Asynchronous

It is natural to think of our code executing sequentially from top to bottom. This is synchronous. However, with JavaScript, some tasks are inherently asynchronous (e.g. setTimeout), and some tasks we design to be asynchronous because we know ahead of time they can be blocking. 

Let’s take a look at a practical example using files in Node.js. If you want to try out the code examples and need a primer on using Node.js, you can find getting started instructions from this tutorial. In this example, we will open a file of posts and retrieve one of the posts. Then we'll open a file of comments and retrieve the comments for that post. 

This is the synchronous way:

index.js

db/posts.json

db/comments.json

The readFileSync method opens the file synchronously. Therefore, we can write our initialization code in a synchronous way as well. But this is not the best way to open the file because this is a potentially blocking task. Opening the file should be done asynchronously so that the flow of execution can be continuous. 

Node has a readFile method we can use to open the file asynchronously. This is the syntax:

We may be tempted to return our data inside of this callback function, but it won’t be available for us to use inside our loadCollection function. Our initialization code will need to change as well because we will not have the correct values to assign to our variables. 

To illustrate the problem, let's look at a simpler example. What do you think the following code will print?

This example will print “second”, “third”, and then "first". It doesn’t matter that the setTimeout function has a 0 delay. It is an asynchronous task in JavaScript, so it will always be deferred to execute later. The firstTask function can represent any asynchronous task, like opening a file or querying our database. 

One solution to get our tasks to execute in the order we want is to use callback functions.

Callback Functions

To use callback functions, you pass a function as a parameter to another function, and then call the function when your task is finished. If you need a primer on how to use higher-order functions, reactivex has an interactive tutorial you can try out. 

Callbacks let us force tasks to execute sequentially. They also help us when we have tasks that depend on the results of a previous task. Using callbacks, we can fix our last example so it will print “first”, “second”, and then “third”.

Instead of printing the string inside each function, we return the value inside of a callback. When our code is executed, we print the value that was passed into our callback. This is what our readFile function uses. 

Returning to our files example, we can change our loadCollection function so that it uses callbacks to read the file in an asynchronous manner.

And this is what our initialization code will look like using callbacks:

One thing to notice in our loadCollection function is that instead of using a try/catch statement to handle errors, we use an if/else statement. The catch block would not be able to catch errors returned from the readFile callback. 

It is good practice to have error handlers in our code for errors that are the result of outside influences as opposed to programming bugs. This includes accessing files, connecting to a database, or making an HTTP request. 

In the revised code example, I did not include any error handling. If an error were to occur at any of the steps, the program would not continue. It would be nice to provide meaningful instructions.

An example of when error handling is important is if we have a task to log in a user. This involves getting a username and password from a form, querying our database to see if it is a valid combination, and then redirecting the user to their dashboard if we are successful. If the username and password were invalid, our app would stop running if we don’t tell it what to do. 

A better user experience would be to return an error message to the user and allow them to retry logging in. In our file example, we could pass an error object along in the callback function. This is the case with the readFile function. Then, when we execute the code, we can add an if/else statement to handle the successful outcome and the rejected outcome.

Task

Using the callback method, write a program that will open a file of users, select a user, and then open a file of posts and print the user’s info and all of their posts.

Summary

Asynchronous programming is a method used in our code to defer events for later execution. When you are dealing with an asynchronous task, callbacks are one solution for timing our tasks so they execute sequentially. 

If we have multiple tasks that depend on the result of previous tasks, one solution is to use multiple nested callbacks. However, this could lead to a problem known as “callback hell.” Promises solve the problem with callback hell, and async functions let us write our code in a synchronous way. In part 2 of this tutorial, we will learn what they are and how to use them in our code.

Resources

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.