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.