Categories
JavaScript Best Practices

JavaScript Best Practices — Promises

Spread the love

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices when defining and using promises.

Catching or Returning

We should either catch or return in our promise functions.

For instance,e we can write:

const foo = () => {
  return myPromise.then(doSomething)
}

or we can write:

myPromise
  .then(doSomething)
  .then(doSomethingElse)
  .catch(errors)

Then we catch errors or return a promise that we can us later.

We Should Return Something or Throw Errors

To make us see the result of our promises more clearly, we should either resolve our value or throw something error value.

For instance, if we write:

myPromise.then(function(val) {
  return val * 2
})

Then we resolve our promise to val * 2 .

If we write:

myPromise.then(function(val) {
  throw 'error'
})

Then we reject the promise with the string 'error' .

This is a good practice because we’ll know the value of whether the promise is successful or not.

Consistent Parameter Names when Creating New Promises

We should have consistent parameter names when we’re creating a promise.

We usually write:

new Promise((resolve) => { ... })

or:

new Promise((resolve, reject) => { ... })

This way, it’s clear which parameter is called for resolving the value and which one to call for rejecting the promise.

Return Inside Each then to Create Readable and Reusable Promise Chains

We should return inside each then callback so that we can use it for something else.

For instance, we can write:

myPromise.then((val) => val * 3));

or:

myPromise.then(function(val) { return val * 3; });

This way, we can call it with then or use await with it.

If we have pre-ES6 code, we should have a Promise Constructor Before Creating a Promise

If we still haven’t moved on to ES6, then we need a 3rd party library to create promises.

A popular choice is Bluebird.

We can use it by writing:

const Promise = require('bluebird')
const x = Promise.resolve('foo')

Don’t Nest then or catch

The whole point of using promises is to avoid nesting, so we shouldn’t nest our promise code.

For example, we should write:

myPromise
  .then(doSomething)
  .then(doSomethingElse)
  .catch(errors)

instead of:

myPromise.then(val =>
  doSomething(val).catch(errors)
)

or:

myPromise.then(val =>
  doSomething(val).then(doSomethingElse)
)

No Promises in Callbacks

We shouldn’t nest promises in callbacks as we can see from the previous example.

Instead, we should use promises to reduce nesting.

So we should write:

myPromise
  .then(doSomething)
  .then(doSomethingElse)
  .catch(errors)

Don’t use new on Promise Static Methods

We shouldn’t use new on Promise static methods.

The only situation when we need new is to create a new Promise without using static methods.

For instance, we should write:

Promise.resolve('foo')

or:

Promise.reject('error')

or:

Promise.race([p1, p2])
Promise.all([p1, p2])

No return Statement in finally

We shouldn’t have return statements in finally since nothing can consume what’s returned.

So we should write:

myPromise.finally((val) => {
  console.log('value:', val)
})

Passing the Correct Number of Arguments into Promise Methods

We should ensure that we pass the correct number of arguments into promise methods.

Promise.all takes 1 argument.

Promise.race takes 1 argument.

Promise.resolve is called with 1 argument.

Promise.rejeect is also called with 1 argument.

Promise.then can be called with 1 or 2 arguments.

Promise.catch is called with 1 argument.

Promise.finally is called with 2 arguments.

If we don’t call them with the right arguments, we would get unexpected results.

Use async/await Instead of then

async/await is a shorter syntax to replace then .

For instance, we can write:

const example = async () => {
  const val = await myPromise()
  val = doSomethingSync(val)
  return doSomethingElseAsync(val)
}

instead of:

myPromise()
.then(val => {
  val =  doSomethingSync(val)
  return doSomethingElseAsync(val)
});

It’s shorter and there’s less nesting.

Instead of calling catch , we can use a try/catch block instead:

const example = async () => {
  try {
    let val = await myPromise()
    val = doSomethingSync(val)
    return await doSomethingElseAsync(val)
  } catch (err) {
    errors(err)
  }
}

The catch block is the same as the catch callback.

Conclusion

We should call catch in our promise chain or return something in our callbacks.

Also, we may consider using async/await instead of the then method for chaining promises.

Static methods shouldn’t be called with the new keyword.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *