Overview
In Javascript, the Promise is ‘an object’ which, according to MDN, is ‘a proxy for a value not necessarily known when the promise is created’. It is an improvement on using callbacks and helps us to avoid the ‘Pyramid of Doom’ while building asynchronous operations. There are several essential features of Promises which will help us to understand exactly how they behave. Of these we will investigate their constructor and prototype methods.
Using the Promise Constructor
In creating a new promise with the promise constructor, we pass it a function with two parameters, ‘resolve’ and ‘reject’. These ‘static methods’ are called on the result of the asynchronous operation being performed in the function which is returning a promise or in the promise body itself. We use ‘resolve’ to return the value out of the Promise when the operation has been successful. We use ‘reject’ to return an error out of the Promise when the asynchronous operation has been unsuccessful. Let’s take a look at building a promise with the promise constructor.
const getStatusCode = (url) => {
return new Promise((resolve, reject) => {
request(url, (err, res) => {
if (err) {
reject(err);
} else {
console.log(res.statusCode);
resolve(res.statusCode);
}
});
});
};
Here we have a function called getStatusCode which takes in a url and returns a promise. Inside of the promise, we use the NodeJS function 'request' to make a request to the url given as an argument to getStatusCode. Request takes a callback in the normal style and when it returns with its result, we will resolve or reject the promise passing in the result as an argument.
Notice how resolve essentially corresponds to the part of the callback that deals with 'data' while reject corresponds to the part that deals with 'error'. However, using promises 'in the wild' looks a little bit different than what we've seen with the constructor and utilizes the prototype methods of the promise object.
Promises in Action
To investigate the prototype methods of the Promise we will need to see it in action. In the below example we will grab the result of the promise, either resolved or rejected, inside of our function via the ‘then’ block(resolved) or ‘catch’ block(rejected). The ‘then’ and ‘catch’ are prototype methods of the promise which allow us to use the values returned out of the promise inside of other asynchronous functions. The then-block will return a function which is supplied with the return value of the previous promise, kind of like the argument for ‘data’ in a callback. The catch-block will return a function which is supplied with the value of the error, kind of like ‘err’ in a callback.
Corresponding to our example above, we will chain on 'then' and 'catch' blocks to capture the values returned by resolve and reject.
getStatusCode('www.google.com')
.then((status) => console.log(status))
.catch(err => console.log(err));
This syntax makes it possible to chain multiple promises together, making for cleaner code than the 'Pyramid of Doom', which refers to the phenomenon of callback hell
instead we use promises. Imagine that 'useStatusCode' is another asynchronous function we have written
getStatusCode('www.google.com')
.then((status) => useStatusCode(status))
.then((data) => console.log(data))
.catch(err => console.log(err));
This keeps our code neat in the event of needing to chain multiple asynchronous functions together.
Conclusion
Promises are a great way to simplify our code. When we create them with the constructor, we use the resolve and reject parameters to signify what we want the promise to do at time of invocation. When we invoke promises or functions that return promises, we work with the return values of the promise (specified as resolve and reject in the constructor) inside the 'then' and 'catch' blocks.
Top comments (0)