In JavaScript, you oftentimes need to perform tasks that will take an undetermined amount of time. Because of this, you want to allow your code to continue running instead of waiting until the task completes. This is called asynchronous programming.

Callback Functions

One of the most popular ways to employ asynchronous programming is through the use of callback functions. As the name implies, a callback function is a function that is called after another function has completed.

Let's look at a simple example:

JAVASCRIPT
function doChore(chore, callback) { alert("Started " + chore + "."); callback(); } function finished() { alert("Finished my chore!"); } doChore("cleaning", finished);

Here you have a function doChore, that takes two parameters, the name of the chore you are doing, and then a function that it will call after the chore is done. We are passing a function that simply alerts the user that the chore is complete, the finished() function. As you would expect, this is the end result:

JAVASCRIPT
alert("Started cleaning."); alert("Finished my chore!");

In reality, anything can happen inside doChore before the callback function is called. Maybe you need to make a request to another server or read a file from disk. The point to get out of this is that the callback function is only called after the rest of function is finished.

We can see this better illustrated when we use the setTimeout function which can add a delay to our code:

JAVASCRIPT
function doChore(chore, callback) { alert("Started " + chore + "."); setTimeout(callback, 3000); } function finished() { alert("Finished my chore after 3 seconds!"); } doChore("cleaning", finished);
JAVASCRIPT
alert("Started cleaning."); alert("Finished my chore after 3 seconds!");

Promises

Promises are used to handle the results of an asynchronous operation, for example like making an API call or reading from disk. We want to continue to allow the code to continue to run and promises are a structured way to handle asynchronous operations.

Creating a Promise

Creating a promise is easy, simply create it like you would any object. It takes a callback function with two parameters, a resolve function and a reject function.

JAVASCRIPT
const promise = new Promise((resolve, reject) => { // implementation here });

Promises only have three possible states they can be in:

  • Pending: A pending Promise hasn't begun its operations yet.
  • Fulfilled: When a promise resolves after its operations finished.
  • Rejected: When a promise fails to complete its operations successfully.

Let's look at an example promise mocking an API call:

JAVASCRIPT
const success = true; const data = new Promise((resolve, reject) => { if (success) { const results = { message: "This was a success!" }; resolve(results); } else { const results = { message: "This failed!" }; reject(results); } });

In this bare-bones example, we are controlling whether or not this mock API data call succeeded with a boolean. Depending on that value, it will determine whether the promise resolves or is rejected.

Using Promises

Now that we have defined our promise, let's try actually using it:

JAVASCRIPT
const success = true; const data = new Promise((resolve, reject) => { if (success) { const results = { message: "This was a success!" }; resolve(results); } else { const results = { message: "This failed!" }; reject(results); } }); data.then(success => { console.log(success.message); }).catch(error => { console.log(error.message); })
HTML
This was a success!

When you use the then function, it receives the value passed in to the resolve function. Likewise, the catch function receives the value passed into the reject function. That is why we are able to immediately access the message value that we passed in earlier.

Async Keyword

Async is keyword in JavaScript that allow you to write promises in an easier and more visually appealing manner. Let's start by declaring a normal function as asynchronous.

JAVASCRIPT
async function apiSuccess() { const results = { message: "This was a success!" }; return results; }

The above code is the equivalent to this:

JAVASCRIPT
function apiSuccess() { const results = { message: "This was a success!" }; return Promise.resolve(results); }

Both of the above functions will resolve to the same exact thing. On the other hand, you can do the same in the reverse:

JAVASCRIPT
async function apiFailure() { const results = { message: "This failed!" }; throw new Error(results); }

That is equivalent to doing this:

JAVASCRIPT
function apiFailure() { const results = { message: "This failed!" }; return Promise.reject(results); }

Await Keyword

The await keyword is used with asynchronous functions to ensure that all the promises are completed and synchronized. This can help remove the need to use callbacks via .then() and .catch(). Here's an example:

JAVASCRIPT
async function getAPIData(url) { try { const response = await fetch(url); const data = await response.json(); console.log(data); } catch(error) { // catches errors in all async functions console.log(error); } } const url = "https://jsonplaceholder.typicode.com/todos/1"; getAPIData(url);
JAVASCRIPT
{ userId: 1, id: 1, title: "delectus aut autem", completed: false }

Our asynchronous API call was successful and we got our data! Alternatively, if you simply wanted to return the data instead of print it out, you can write it like this:

JAVASCRIPT
async function getAPIData(url) { try { const response = await fetch(url); const data = await response.json(); return data; } catch(error) { return error; } } const url = "https://jsonplaceholder.typicode.com/todos/1"; const data = await getAPIData(url); console.log(data);
JAVASCRIPT
{ userId: 1, id: 1, title: "delectus aut autem", completed: false }

Finally, if you want to call an async function from a non-async function, you can use the then method. Here's an example:

JAVASCRIPT
async function getAPIData(url) { try { const response = await fetch(url); const data = await response.json(); return data; } catch(error) { return error; } } function printAPIData(url) { getAPIData(url).then(result => { console.log(result); }); } const url = "https://jsonplaceholder.typicode.com/todos/1"; printAPIData(url);
JAVASCRIPT
{ userId: 1, id: 1, title: "delectus aut autem", completed: false }

Conclusion

Hopefully you have seen how useful JavaScripts asynchronous features can be. They allow you to rewrite code that previously were constant callbacks into something much more readable and semantic. They also allow you to write code that is easier to test and maintain.

Resources

Next Lesson »
Copyright © 2017 - 2024 Sabe.io. All rights reserved. Made with ❤ in NY.