Patterns for Dealing with Async Code

We are going to use setTimeout to simulate asynchronous call to a database and async network requests. Consider the following scenario:

  • We look up a user in our database to collect its bank account number.
  • We send a HTTP GET request to a bank, providing the account number, to receive a list of loans associated with the given account.
  • After the list of loans were received, we will send a second request to get a complete transaction history (e.g. payments, etc) associated with the most recent loan.

Don't get hung up on the irrelevant details such as what if there are no loans associated with the account, etc.

Let's start by (simulation of) getting a user object from a database.

function getUser(id) {
  console.log("Reading a user from a database...");
  setTimeout(() => {
    console.log("Received user data...");
    return { "ID": id, "Account number": "58721094531267" }
  }, 2000);
}

console.log("listening for events");
getUser(1);
console.log("still listening for events!");

Let's try to store and display the user returned from the database:

function getUser(id) {
  console.log("Reading a user from a database...");
  setTimeout(() => {
    console.log("Received user data...");
    return { "ID": id, "Account number": "58721094531267" }
  }, 2000);
}

console.log("listening for events");
const user = getUser(1);
console.log(`user: ${user}`);
console.log("still listening for events!");

The user object is undefined because the return statement in getUser is executed (at least) two seconds after it was called. So what is returned from getUser will not be available at the time of calling it.

So how can we access the user returned from getUser function? There are three patterns to deal with asynchronous code:

  • Callbacks
  • Promises
  • Async/await

We will look at each in the following sections.