Photo by Bharat Patil on Unsplash

JavaScript Never Breaks a Promise

This week, I’ve built a mood and emotions logging application using a Rails API backend and a JavaScript frontend. Read on to learn about Promises and to find out how I used JavaScript Promises to manage the asynchronous functions of the application! But first… here’s what I’ve built:

Feels

Home Page of Feels

Feels allows users to track how they are feeling. With a simple form, users can rate how they are feeling from 1–100 and then add different emotions to their entry. User can then view their past entries and see how their mood has changed over time and to remembers the good and bad moments they’ve had in the past.

One of the core UI elements of Feels is the use of modals:

Example of the a modal in the entry form

Modals are a great way to hide content until the user performs an action which then triggers a separate window to open up on top of the main page. Here, I use a modal to open up a panel that allows users to add/remove emotions from their entry. Users can easily interact and scroll in this panel without interacting with the main page and then once they are done, click the x in the top right or click outside the panel to close the modal.

If you want to check out Feels yourself, feel free to download/clone the project from GitHub.

What is a Promise?

The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value. — JavaScript Docs

Simply put, a Promise wraps up some asynchronous operations into its own Object. What this does is allow you to access the final state and result of those operations after they have taken place. This gives a lot of power when working with asynchronous functions, enough so that all asynchronous functions in JavaScript return a Promise.

Here are some great things about Promises that will help show how and why they are used:

#1 — Promises guarantee the execution of asynchronous operations

Sometimes with asynchronous functions, we want to wait until the functions have executed before calling other function. For example, we may want to request all of our Blogs from our backend API before rendering blogs on the page.

fetchBlogs() // Asynchronous function
renderBlogs() // Synchronous function

This is what our code may look like if we wrote this in a synchronous way. We called fetchBlogs(), which is an asynchronous function. That means as our application is performing that operation, we call renderBlogs() before confirming that we finished fetching our blogs. This should raise some serious alarms in your head!

What if we try to render all our blogs before we finish fetching them? Well, there would be no blogs to render! Here’s a possible fix, using the power of promises:

fetchBlogs()
.then(renderBlogs)

How does then() magically fix our problem? Well, then() is an instance method we can call on a Promise (remember that all asynchronous functions return a Promise). It ensures us as programmers that the operations wrapped up in the Promise calling then() will always be executed before. That means, fetchBlogs() will always finish executing before renderBlogs() is called.

#2 — Promises allow us to chain asynchronous operations

The then() method also allows us to create something called a promise chain, which is a sequence of asynchronous operations that will execute one after the other, like synchronous functions. That’s because then() returns — you guessed it — another Promise!

Here’s an example:

fetchBlogs()
.then(renderBlogs)
.then(colorBlogs)
.then(removeBlueBlogs)

So what’s happening here? First off, assume that all of these functions are asynchronous functions. Now, because we know that then() returns a Promise we can keep chaining calls to then() providing a different callback operation each time. By creating a promise chain, we ensure that our operations will be performed in order.

#3 — Promises can handle both success and failure

then() also allows us to provide a second callback function that will be called if the previous Promise has a rejected state.

fetchBlogs()
.then(renderBlogs,logFetchError)

Here we can use then() to handle both fulfilled and rejected Promises. However, more often than not, we will see the following used instead:

fetchBlogs()
.then(renderBlogs)
.catch(logFetchError)

catch() is very similar to then() except it’s callback function will only be used in the case of a rejected Promise. Internally, JavaScript actually called then(undefined, callbackRejection) when catch() is used.

How I used Promises to manage fetch requests

The following are a series of snippets of code that is used to fetch entries from a database, fetch the emotions associated with each entry from a database, and then render that data onto the page:

index.js
models/entry.js
models/emotion.js

Note that fetch() is an asynchronous function and thus returns a Promise.

The start of the loadEntries() function is a standard fetch request but I want to highlight how createEntries() in line 69 uses promises.

Moving into the entry.js file, we see that it returns the result of Promise.all(...). This method wraps up an array of promises into a single Promise that guarantees the execution of each of the promises in the list before any other functions that are chained on.

I do this here because in order to create a new Entry object, I need to perform another fetch request, wait for that request to be fulfilled and then use that result to create the Entry.

Following the promise chain, we see that on line 54 in entry.js we call Emotion.getEmotions() which returns another Promise that ensures we fetch all the Emotion data we need from our backend (lines 44–48 in emotion.js). Once that Promise is fulfilled, then we execute lines 55–57 in entry.js. That is where we finally create our new Entry object.

Because I wrapped all of these operations in promises, I can ensure that everything is happening in the order that I want. Thus, only after I’ve fetched all of the Entry data, fetched all of the Emotion data, created new Emotion objects, created new Entry objects, will I then execute line 70–72 in index.js, which is where I render the entries.

Thanks for reading!

If you’ve made it all the way here, you can check out more about promises in the official documentation on Using Promises.

I hope this was helpful and showed you how to implement promises into your own projects!

Software Engineer helping companies bring new products to life. Follow along with my coding journey here!