JavaScript Promises - Understand JavaScript Promises by Building a Simple Promise Example

A step-by-step tutorial to make sure you fully understand how JavaScript promises work by building a promise from scratch

Trey Huffine
Level Up Coding

--

In this tutorial you will learn JavaScript promises by walking through building a JavaScript promise from scratch. This lesson is great for beginners to intermediate developers who are still learning the fundamentals of asynchronous JavaScript. The JavaScript promise example you will build is meant to help you understand the basics of promises and asynchronous thinking — it is not intended to show the most optimized version of a promise.

You’ve probably seen something like this before:

fetch('/user/1')
.then((user) => {
/* Do something with user after the API returns */
})

The block of code in the .then() waits until it receives the response from the server before it executes anything. This is called a Promise. But don’t let the fancy name or the fact that there is asynchronous code intimidate you — a Promise is just a plain old JavaScript object with special methods that allow you to execute code synchronously (it will do things in order even though there is a delay).

typeof new Promise((resolve, reject) => {}) === 'object' // true

Let me reiterate (because this is something that was difficult for me to grasp when I first learned promises), a Promise is just an object. To be able to wait on the server and execute the code in the .then() chain after the response, you MUST return a Promise object. This is not something functions get out of the box. Behind the scenes, the fetch function is doing something like this.

const fetch = function(url) {
return new Promise((resolve, reject) => {
request((error, apiResponse) => {
if (error) {
reject(error)
}

resolve(apiResponse)
})
})
}

The fetch() function makes an http request to the server, but the client doesn’t know when the server will send back the results. So JavaScript begins executing other unrelated code while waiting on the server to return with the response. Once the client receives the response, it initiates the execution of the code in the .then() statements by calling resolve(apiResponse).

Now let’s take a closer look at how the Promise actually allows you to do this.

Educational example of a Promise — https://gist.github.com/treyhuffine/d2e63bdee6645a7a0619989ee5a4538b

NOTE: This version of a Promise is for educational purposes only. I’ve left out some of the more advanced features and distilled it to its core functionality.

I’ve named it PromiseSimple so it won’t clash with the native Promise in case you want to copy and paste it into your Chrome console. Our promise implementation has a constructor, 2 public methods that you may be familiar with then() and catch(), and 2 internal methods onResolve() and onReject().

When you create a promise, you do so like this new Promise((resolve, reject) => {/* ... */}). You pass it a callback function which I’ve named executionFunction in the constructor. The execution function takes a resolve and reject which map to the internal onResolve() and onReject() function. These are the functions that will be called when the fetch calls the resolve or reject.

The constructor also creates a promiseChain array and handleError function. When a series of .then(() => {}) are added, it pushes each function onto the promiseChain. When a user calls catch(() => {}), it assigns the function to the internal handleError. Notice that the then() and catch() function return this;. This allows you to chain multiple then()’s since you’re returning the object itself.

NOTE: In the native Promise, these then() and catch() functions actually return a new Promise themselves, but for this simple scenario, I’ve only returned this. In addition, there can be multiple .catch() blocks and they can be chained as well and aren’t required to come at the end of the .then() chain.

When your asynchronous function calls resolve(apiResponse), the promise object then begins executing onResolve(apiResponse). It iterates through the entire promiseChain by removing the function at the front and executes it with the most recent value saved in storedValue. It then updates storedValue to the result of the most recent execution. It will execute these functions in order. This creates the synchronous promise chain.

This loop is wrapped in a try/catch block. This is special JavaScript syntax that looks for errors. If your asynchronous function calls reject(error) or your try/catch recognizes an error, it will then be passed to the onReject() method which calls the function that you passed to .catch().

Putting it all together with a more practical example:

https://gist.github.com/treyhuffine/f21525172fece828d385f9c5db8f87a0

If you found this article helpful, please tap the 👏. Follow me for more articles on React, Node.js, JavaScript, and open source software! You can also find me on Twitter or gitconnected.

--

--

Founder | Software Engineer. Building products that empower individuals.