3 most common mistakes when using Promises in JavaScript
Tags: javascript
Published: 2020-08-01 08:27:28

Promises rule JavaScript. Even nowadays, with introduction of async/await, they are still an obligatory knowledge for any JS developer.

But JavaScript differs in how it deals with asynchronicity from other programming languages. Because of that, even developers with a lots of experience can sometimes fall into its traps. I have personally seen great Python or Java programmers making very silly mistakes when coding for Node.js or browsers.

Promises in JavaScript have many subtleties which one has to be aware of in order to avoid those mistakes. Some of them will be purely stylistic, but many can introduce actual, difficult to track errors. Because of that, I have decided to compile a short list of the three most common mistakes I have seen developers do, when programming with Promises.

### Wrapping everything in a Promise constructor

This first mistake is one of the most obvious, and yet I have seen developers do it surprisingly often.

When you first learn about Promises, you read about a Promise constructor, which can be used to create new Promises.

Perhaps because people often start learning by wrapping some browser APIs (like `setTimeout`) in the Promise constructor, it gets ingrained in their minds that the only way to create a Promise is to use the constructor.

So as a result they often end up with a code like this:

```js
const createdPromise = new Promise(resolve => {
  somePreviousPromise.then(result => {
    // do something with the result
    resolve(result);
  });
});
```

You can see that in order to do something with the `result` from `somePreviousPromise` someone used `then`, but later decided to wrap it again in a Promise constructor, in order to store that computation in the `createdPromise` variable, presumably in order to do some more manipulations on that Promise later.

This is of course unnecessary. The whole point of `then` method is that it itself returns a Promise, that represents executing `somePreviousPromise` and then executing a callback passed to the `then` as an argument, after `somePreviousPromise` gets resolved with a value.

So the previous snippet is roughly equivalent to:

```js
const createdPromise = somePreviousPromise.then(result => {
  // do something with result
  return result;
});
```

Much nicer, isn’t it?

But why I wrote that it is only roughly equivalent? Where is the difference?

It might be hard to spot for the untrained eye, but in fact there is a massive difference in terms of error handling, much more important than the ugly verbosity of the first snippet.

Let’s say that `somePreviousPromise` fails for any reason and throws an error. Perhaps that Promise was making a HTTP request underneath and an API responded with a 500 error.

It turns out that in the previous snippet, where we wrap a Promise into another Promise, we have no way to catch that error at all. In order to fix that, we would have to introduce following changes:

```js
const createdPromise = new Promise((resolve, reject) => {
  somePreviousPromise.then(result => {
    // do something with the result
    resolve(result);
  }, reject);
});
```

We simply added a `reject` argument to the callback function and then used it by passing it as a second parameter to the `then` method. It’s very important to remember that `then` method accepts second, optional parameter for error handling.

Now if `somePreviousPromise` fails for any reason, `reject` function will get called and we will be able to handle the error on `createdPromise` as we would do normally.

So does this solve all of the problems? Unfortunately no.

We handled the errors that can occur in the `somePreviousPromise` itself, but we still don’t control what happens within the function passed to the `then` method as a first argument. The code that gets executed in the place where we have put the `// do something with the result` comment might have some errors. If the code in this place throws any kind of error, it will not be caught by the `reject` function placed as a second parameter of the `then` method.

That’s because error handling function passed as a second argument to `then` only reacts to errors that happen earlier in our method chain.

Therefore, the proper (and final) fix will look like this:

```js
const createdPromise = new Promise((resolve, reject) => {
  somePreviousPromise.then(result => {
    // do something with the result
    resolve(result);
  }).catch(reject);
});
```

Note that this time we used `catch` method, which — because it gets called **after** the first `then` — will catch any errors that get thrown in the chain above it. So whether the `somePreviousPromise` or the callback in `then` will fail — our Promise will handle it as intended in both of those cases.

As you can see, there are many subtleties when wrapping code in Promise constructor. That’s why it’s better to just use `then` method to create new Promises, as we have shown in a second snippet. Not only it will look nicer, but we will also avoid those corner cases.

### Consecutive thens vs parallel thens

Because many programmers have Object Oriented Programming backgrounds, it’s natural for them that a method mutates an object rather than creates a new one.

It’s probably why I see people being confused about what exactly happens when you call a `then` method on a Promise.

Compare those two code snippets:

```js
const somePromise = createSomePromise();

somePromise
  .then(doFirstThingWithResult)
  .then(doSecondThingWithResult);
```

```js
const somePromise = createSomePromise();

somePromise
  .then(doFirstThingWithResult);

somePromise
  .then(doSecondThingWithResult);
```

Do they do the same thing? It might seem so. After all, both code snippets involve calling `then` twice on `somePromise`, right?

No. It’s a very common misconception. In fact, those two code snippets have a completely different behavior. Not fully understanding what is happening in both of them can lead to tricky mistakes.

As we wrote in a previous section, `then` method creates a completely new, independent Promise. This means that in the first snippet, second `then` method is not being called on `somePromise`, but on a new Promise object, that encapsulates (or represents) waiting for `somePromise` to get resolved and then calling `doFirstThingWithResult` right after. And then we add a `doSecondThingWithResult` callback to this new Promise instance.

In effect, the two callbacks will be executed one after another — we have a guarantee that the second callback will be called only after the first callback finishes execution without any issues. What is more, the first callback will get as an argument a value returned by `somePromise`, but the second callback will get as an argument whatever is returned from the `doFirstThingWithResult` function.

On the other hand, in the second code snipped, we call `then` method on `somePromise` twice and basically ignore two new Promises that get returned from that method. Because `then` was called twice on exactly the same instance of a Promise, we don’t get any guarantees about which callback will get executed first. The order of execution here is undefined.

I sometimes think about it as “parallel” execution, in a sense that the two callbacks should be independent and not rely on any of them being called earlier. But of course in reality JS engines execute only one function at a time — you simply don’t know in which order they will be called.

The second difference is that both `doFirstThingWithResult` and `doSecondThingWithResult` in the second snippet will receive the same argument — the value that `somePromise` gets resolved to. Values returned by both the callbacks are completely ignored in that example.

### Executing a Promise immediately after creation

This misconception also comes from the fact that most coders are often experienced in Object Oriented Programming.

In that paradigm, it is often considered a good practice to make sure that an object constructor does not perform any actions by itself. For example an object representing a Database should not initiate the connection with the database when its constructor is called with the `new` keyword.

Instead, it’s better to provide special method — for example called `init` — that will explicitly create a connection. This way an object does not perform any unintended actions only because it was initiated. It patiently waits for a programmer to explicitly ask for executing an action.

But that’s not how Promises work.

Consider the example:

```js
const somePromise = new Promise(resolve => {
  // make HTTP request
  resolve(result);
});
```

You might think that the function making an HTTP request does not get called here, because it is wrapped in a Promise constructor. In fact, many programmers expect that it gets called only after a `then` method gets executed on a `somePromise`.

But that’s not true. The callback gets executed immediately when that Promise is created. It means that when you are in the next line after creating `somePromise` variable, your HTTP request is probably already being executed, or at least scheduled.

We say that a Promise is “eager” because it executes an action associated with it as fast as possible. In contrast, many people expect the Promises to be “lazy” — that is to perform an action only when it is absolutely necessary (for example when a `then` gets called for the first time on a Promise). It’s a misconception. Promises are always eager and never lazy.

But what you should do if you want to execute the Promise later? What if you want hold off with making that HTTP request? Is there some magic mechanism built into the Promises that would allow you to do something like that?

The answer is more obvious than the developers sometimes would expect. Functions are a lazy mechanism. They are executed only when programmer explicitly calls them with a `()` bracket syntax. Simply defining a function doesn’t really do anything just yet. So the best way to make a Promise lazy is… to simply wrap it in a function!

Take a look:

```js
const createSomePromise = () => new Promise(resolve => {
  // make HTTP request
  resolve(result);
});
```

Now we wrapped the same Promise constructor call in a function. Because of that nothing really gets called yet. We also changed a variable name from `somePromise` to `createSomePromise`, because it is not really a Promise anymore — it is a function creating and returning a Promise.

The Promise constructor — and hence the callback function with a HTTP request — will only be called when we execute that function. So now we have a lazy Promise, that gets executed only when we really want it.

What is more, note that for free we got another capability. We can easily create another Promise, that performs the same action.

If for some weird reason we would like to make the same HTTP call twice and execute those calls concurrently, we can just call the `createSomePromise` function twice, one immediately after another. Or if a request fails for any reason, we can retry it, using the very same function.

This shows that it’s extremely handy to wrap Promises in functions (or methods) and hence it is a pattern that should become natural for a JavaScript developer.

Ironically, if you have read my article on [Promises vs Observables](https://medium.com/@mpodlasin/promises-vs-observables-4c123c51fe13), you know that programmers being introduced to Rx.js often make an opposite mistake. They code Observables as if they are eager (like Promises), while in fact they are lazy. So, for example, wrapping Observables in a function or a method often does not make any sense and in fact can even be harmful.

### Conclusion

I have shown you three types of mistakes that I have often seen being made by developers who knew Promises in JavaScript only superficially.

Are there any interesting types of mistakes that you have encountered either in your code or in the code of others? If so, share them in the comment.

If you enjoyed this article, considered following me on [Twitter](https://twitter.com/m_podlasin), where I will be posting more articles on JavaScript programming.

Thanks for reading!

(<span>Photo by <a href="https://unsplash.com/@officestock?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Sebastian Herrmann</a> on <a href="https://unsplash.com/s/photos/headache?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>)