I promise, you will understand Promise after reading this (Part II)
04 Aug 2019
Reading time ~4 minutes
How can I use Promise?
In Promise Part I, I gave a general overview on why Promise
would be such a lifesaver when dealing with async operations in JavaScript.
Now let’s look into how we can go about utilizing this amazing feature.
Constructing a Promise Instance
Promise
turns an async operation into an instance through Promise constructor
.
Promise constructor
takes a callback function as an argument, and that callback function takes in two functions called resolve
and reject
.
var promiseInstance = new Promise(function (resolve, reject) {
// async operation here
if (/* async operation succeeds */) {
resolve(/* result */);
} else { /* async operation fails */
reject(/* failure reason */);
}
});
The callback function passed in as an argument in Promise constructor
does async operations inside, and invokes resolve
with the result of async operation passed in as its argument if the operation was successful. reject
, on the other hand, is invoked with the failure reason when the operation fails.
Let’s fill in some blanks and make the above code look more legitimate.
(below example is from poiemaweb)
const promiseAjax = (method, url, payload) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url); // method: kind of task, url: place where I can find this info
xhr.setRequestHeader('Content-type', 'application/json');
xhr.send(JSON.stringify(payload));
xhr.onreadystatechange = function () {
// if fetch operation is not complete, ignore.
if (xhr.readyState !== XMLHttpRequest.DONE) return;
if (xhr.status >= 200 && xhr.status < 400) {
// call resolve method to pass the result
resolve(xhr.response); // Success!
} else {
// call reject method to pass the error message
reject(new Error(xhr.status)); // Failed...
}
};
});
};
promiseAjax('GET', 'http://jsonplaceholder.typicode.com/posts/1')
promiseAjax
stores a function that returns a new Promise
instance.
The result of the async operation is passed into resolve
method if the operation is successfully carried out. If the operation fails, the error message is passed into reject
method.
We must be able choose what to do with result or the error message, and this is where .then
and .catch
method come into play.
Handling result and error of a Promise instance
.then
The above async function passes the result into resolve
method. This means that promiseAjax('GET', 'http://jsonplaceholder.typicode.com/posts/1')
this line of code is referencing to a newly created Promise
instance that is passing the result via resolve
method.
The result can be handled by chaining .then
method to the Promise
instance.
then
method takes up to two arguments, which are both callback functions. The first callback is invoked when resolve
is invoked in the Promise
instance, and the second callback is invoked when reject
is invoked in the Promise
instance.
then
method by default returns a Promise
instance.
Depending on the return value of the callback/handler, the Promise
returned by then
either resolves or rejects with the returned value. There are several cases regarding this. Check MDN .then for the list of behaviors.
promiseAjax('GET', 'http://jsonplaceholder.typicode.com/posts/1')
.then(function (result) {
return JSON.parse(result);
},
function (errorMessage) {
return console.error(errorMessage);
});
The first callback in then
receives result
that was passed from the Promise
instance from promiseAjax
. The callback then returns JSON.parse(result). Promise
instance returned by then
gets resolved with this return value of the callback!
This can be quite a lot to take in so let’s divide and conquer.
.then
method returns aPromise
instance- A
Promise
instance shouldresolve
orreject
depending on the outcome of the task. - If the callback returns a value, the above
Promise
instance created bythen
method resolves with that returned value of the callback. (There are also cases where the callback returns nothing, or throws an error, etc.)
Interesting right?
What if the Promise
instance that then
is being invoked has already been rejected?
No worries! According to MDN, if a handler function returns an already rejected promise, the promise returned by then
gets rejected with that promise’s value as its value.
There’s a whole list on what happens to the Promise
instance returned by then
when either the callback is called, so make sure to check it for yourself.
.catch
We have already seen that then
‘s second argument, a callback that is invoked when Promise
instance invokes reject
, "catches" error. However, this callback will only “catch” the errors that occur in async operations (a.k.a. when reject
is invoked). However, it is possible that errors occur inside then
methods as well (when Promise
instance created by then
doesn’t invoke reject
). And that’s when catch
can become handy.
promiseAjax('GET', 'http://jsonplaceholder.typicode.com/posts/1')
.then(JSON.parse)
.then(render)
.catch(console.error);
(code example from poiemaweb)
Other Promise methods
Promise.resolve & Promise.reject
Promise.resolve
and Promise.reject
turns a value into a Promise
instance.
const resolvedPromise = Promise.resolve([1, 2, 3]);
resolvedPromise.then(console.log); // [ 1, 2, 3 ]
//above is same as below
const resolvedPromise = new Promise(resolve => resolve([1, 2, 3]));
resolvedPromise.then(console.log); // [ 1, 2, 3 ]
const rejectedPromise = Promise.reject(new Error('Error!'));
rejectedPromise.catch(console.log); // Error: Error!
//above is same as below
const rejectedPromise = new Promise((resolve, reject) => reject(new Error('Error!')));
rejectedPromise.catch(console.log); // Error: Error!
(code example from poiemaweb)
There’s also Promise.all
and Promise.race
, but I will not go into detail on what these methods do.
These are also very interesting methods in Promise
, so check them out for yourself!
You can begin here: MDN Promise.all, MDN Promise.race.