How to make and break Promises in JavaScript

Before we start, I want you to write an essay about everything you plan to do once you’ve learned how to make a promise. Ready? Great.

You may begin.

But you’re here because you’re tired of having to do that all the time in JavaScript. You’re here because you‘re tired of callbacks and contingency planning. You just want something to pass along to the next person and you couldn’t care less about when they get it.

You need to know two terms. You’re probably thinking you already know what these words mean, and you’re probably right.

Resolve and Reject. Resolve and Reject. Reject and Resolve. Resolve and Reject. pRRomises.

You resolve a promise when your value is ready. When your value is ready, your promise has been resolved.

You reject a promise when an error occurs. When an error happens, your promise has been rejected.

Promises: Resolve and Reject.

Making Promises

Promises are easy to make. In fact, you’ve ever even dabbled with promises before, you’ve already made plenty. Why?

A promise in the making.

We just made a new promise. It’s not the one called promise. It works like this:

  1. The original promise resolves a value.
  2. That value is given to .then.
  3. .then, like any ordinary function, returns a value.
  4. JavaScript makes a new promise, resolved with .then’s return value.

So let’s save that promise and .then it.

This prints, “I’m done with my thing!”

Wow, easy! You actually made a promise! If it walks like a promise, and it talks like a promise, then it’s a promise, right?

Yay! We’re done!

“But I thought you were going to tell me how to make a new Promise.”

This gets a little harder. First, tell JavaScript that you want to make a new Promise. You need to provide a callback. Don’t worry though — JavaScript executes it immediately.

Why a callback, then?

It’s the easiest way to provide you with two functions that you’ll need to call. Think of them as keywords. Can you guess what they are?

That was actually mostly comments.

That’s right. The two functions you get are called resolve and reject. Surprise.

If you’ve ever implemented your own callbacks before, this should seem extremely familiar to you. It works exactly the same way, except this time around, you have two callbacks to pick from, and JavaScript is the one providing them to you.

  • Call the resolve callback to provide a value to anyone who wants it.
    You can pass one argument, but that argument can be anything you want.
  • Call the reject argument to tell them that you can’t provide it.
    You should pass one argument, which is probably going to be an error. Generally, this is stuff you want to throw.

Are we done? Is that every method of creating a promise?

No, there are four more. Let’s start with the easier two.

// This takes your value, creates a promise, and then
// immediately rejects it with your value.
let rejectedPromise = Promise.reject(new Error('An error'))

Creating an instantly rejected promise might be useful if you’re creating a function that returns a promise, but you encounter an error before you even get to the asynchronous part.

Alternatively, you could just throw an error, but it’s up to you and your implementation requirements.

// This takes your value, creates a promise, and then
// immediately resolves it with your value.
let resolvedPromise = Promise.resolve("A value")

Wrapping values inside promises seems useless at first glance, until you realize that this also works:

// This takes your promise and gives you the same promise back.
let redundantPromise = Promise.resolve(promise)

Imagine you’re writing a function. You take some data as an argument, but you don’t know if your data is a value or a promise. You can easily fix that:

Just make everything a promise.

The final methods: com-promise-s

The other two methods of creating promises are pretty similar. They both take an array (or technically, any iterable) of promises and give you a single promise back.

No more nested callbacks and flagging variables to achieve the same thing.

With Promise.all, you want all of your promises to succeed. If any of them fails, everything fails. Otherwise, you get an array of values.

With Promise.race, you want them to race each other and you only get the winner’s value. If any of them fail, they all still fail. Failure is bad.

  • Promise.all resolves when all of its component promises have resolved.
    It rejects if any of its component promises reject.
    It resolves with an array of all of the values, but rejects with a single error.
  • Promise.race resolves when any of its component promises resolve.
    It also rejects if any of its component promises reject.
    It resolves with a single value, and rejects with a single error.

Fun with promises

You now know everything that you need to know to create promises. You know how to create them, and how to execute things in serial or parallel.

Now you’re ready to learn about a couple interesting things you can do with promises.

Resolving promises

Resolving a promise passes it on. This works whether that promise is currently pending, already resolved, or prematurely rejected.

Don’t do this.
Do this instead.

Chaining promises

You already know that when you call .then, it creates a new promise that is immediately resolved with .then’s callback’s return value. It’s a little like a Promise.resolve that waits for another promise.

So that means…

Your .then callback can return more promises that don’t execute until your last promise resolved, and you can write one handler at the very bottom that catches all the errors that might have happened up to that point.

You can even return another promise from .catch to return to regular, non-rejected programming.

Callback hell

Promises were supposed to help with this, right? How, exactly?

This does the exact same thing as before, but with more indents.

If you compare this code snippet with the last one, you’ll see that the last one took up more lines, but it’s definitely more sustainable. If you realize you missed a step, it’s just a matter of adding an extra .then statement instead of having to wrangle with your indentation scheme.

Or more commonly, if you wanted to add an extra action to the end of your callback tower, you had to widen your text editor even more and get ready to tack on a couple extra indentation layers. Callback based code is hard to use and scale.

So does that mean you have to rewrite all your code?

You don’t. It’s very easy to convert callback based functions into promise based ones. You can even maintain backward compatibility.

You can turn your callback functions into promise ones without rewriting anything.

All you need to do is wrap your entire code block inside a promise, and plop in a resolve where you would place a callback.

If you want to maintain backward compatibility, add a .then to your promise with your callback in it. If you do so, make sure you return your original promise, and not the return value from .then.

Do you know why that is? I explained above.

With your callback handler wrapped in a promise, adding more actions to the end is easy and stress free.

Callback hell feels like a distant memory now, doesn’t it?

You’re done!

If you made it this far, you should be all ready to go forth and make some promises! In fact, I started working with them without even knowing half of this stuff.

If you ever get lost or need to check a reference, I find Mozilla’s website to be really good at that kind of stuff these days!

Wait wait wait! This .then thing is really annoying! I’m tired of wrapping stuff all the time!

A lot of us agree. And we have a proposed solution that will hopefully eventually make it into JavaScript!

await is coming.
Future JavaScript Promises: there’s no .then