Since 2015, JavaScript has improved immensely.
It’s much more pleasant to use it now than ever.
In this article, we’ll look at JavaScript promises.
Benefits of Promises
Promises are chained.
And they look similar to synchronous code.
For example, we can write:
asyncFunction(arg)
.then(result1 => {
console.log(result1);
return asyncFunction2(x, y);
})
.then(result2 => {
console.log(result2);
});
We return a promise in the then
callback to let us call then
again.
Chaining is also simple since we just call then
until we’re done with running the promises we want.
Composing async calls is easier since we can do the chaining.
Error handling can be done with the catch
method.
For instance, we can write:
asyncFunction(arg)
.then(result1 => {
console.log(result1);
return asyncFunction2(x, y);
})
.then(result2 => {
console.log(result2);
})
.catch(error => {
console.log(error);
})
The catch
method takes a callback that has an error
parameter.
So we can do whatever we want with them inside the callback.
The function signatures are clear and we can see the parameters easily.
Promises is also a standard way to write async code.
Before promises are introduced to ES6 as a native features, there’re many libraries that implement the same thing.
But now, ES6 makes promises standard so we can use it anywhere.
Creating Promises
To create a promise, we can use the Promise
constructor.
For instance, we can write:
new Promise((resolve, reject) => {
setTimeout(() => resolve('DONE'), 100);
});
We use the constructor with a function that takes the resolve
and reject
parameters.
resolve
is a function that’s called to return the value when it succeeds.
reject
is a function that’s called to return the reason why the promise fails.
A promise is a container for value and an event emitter.
Promises looks like synchronous code, but they’re non-blocking.
They can’t return anything synchronously.
ES6 introduced a way for us to suspend and resume code with generators.
Therefore, this is used as the foundation for promises and async functions in JavaScript.
For instance, if we have:”
async function main() {
const x = await asyncFunc();
console.log(x);
//...
}
main();
asyncFunc
returns a promise and runs the console log after the function is called.
await
is like yield
in JavaScript generator functions.
It does the same thing. It pauses the code and then continues running when the result is retrieved.
Using Promises
Promises have 3 states.
They can be pending, fulfilled, or rejected.
Pending means that the result hasn’t been computed.
Fulfilled means the result was computed successfully.
Rejected means a failure occurred during computation.
The parameter we pass into the Promise
constructor is called an executor.
In addition to calling reject
to make a promise fail, we can also reject a promise with an exception.
Consuming a Promise
To consume a promise, we can call then
to get the resolved result from a promise.
The callback we pass into then
takes the resolved results of a promise.
catch
takes a callback which takes the argument we passed into reject
.
For example, we can write:
promise
.then(value => {
//...
})
.catch(error => {
//...
});
value
has the resolved value of promise
.
error
has the rejected value.
then
can also take 2 arguments.
One is the resolved callback and the 2nd argument is the rejected callback.
So we can also catch errors by writing:
promise.then(
null,
error => {
//...
});
The 2nd argument is the same as the catch
callback and catches the error of the promise
.
Conclusion
Promises have many benefits, and we can create and consume them in a standard way.