Promises in JavaScript
Peter Klingelhofer
Posted on July 19, 2020
Introduction
Callbacks are functions that run after something happens or something completes. If you have to make an asynchronous request, it can either be successful or result in failure. These two possibilities in Promises are denoted by resolves
or rejects
parameters.
Promises help us avoid what's commonly referred to as "callback hell", wherein cascading callbacks create a pyramid shape and impossible to decipher code that's hard to scale. Promises allow us to easily chain callbacks in an intuitive way, by chaining then
and using catch
for errors. Promises helps to break up callbacks into methods with semantic naming.
A Simple Promise
const promise = new Promise(function (resolve, reject) {
const a = 'apples';
const b = 'apples';
if (a === b) {
resolve();
} else {
reject();
}
});
promise
.then(function () {
console.log('success');
})
.catch(function () {
console.log('error');
});
// output => 'success'
Concepts to Know
- Deferred objects are works that are not yet completed
- Promise properties are placeholders. Initially, this value is unknown.
- Each deferred has a promise which acts as a placeholder for the future result.
Support
All modern browsers support Promises, except for Internet Explorer 11 and previous versions.
API Request Example with Promises
Let's pretend that we have a website with multiple accounts created for people's pets. Below, we create a promise to request a made up api server a specified number of pets' user accounts. If the statusCode associated with the request is 200, that indicates that the request was successful, and that we should call resolves
to utilize JSON.parse
to parse the results. If we do not receive a 200 statusCode back, rejects
is called, showing the user the failing statusCode.
// creating the request
const getPets = (number) => {
new Promise((resolves, rejects) => {
const request = new XMLHttpRequest();
request.open("GET", `https://api.examplepet.com/?results=${number}`);
request.onload = () => {
if (request.status === 200) {
resolves(JSON.parse(request.response).results);
} else {
rejects(Error(request.statusText));
}
request.onerror = (error) => rejects(error);
request.send();
};
});
};
// invoking the request
getPets(20)
.then((pets) => console.log(pets))
.catch((error) => console.error(error));
Promise Method: done()
Normally, when chaining method calls with Promises, you can risk discarding errors without seeing them:
function promisesExampleFunction() {
asyncFunc()
.then(function1)
.catch(response1)
.then(function2);
}
If the last .then(function2);
line elicits a rejection, it is never handled. We can account for this using done()
like so:
function promisesExampleFunction() {
asyncFunc()
.then(function1)
.catch(response1)
.done(function2);
}
In the example above, done()
can actually replace the last .then(function2);
, or it can be interchangeably added after the last .then(function2)
with done()
receiving no arguments.
Promise Method finally()
finally()
is great for performing actions regardless of whether an error occurs. It is most frequently used as a clean up method after the last then()
or done()
call. This can be done during page loads to show and hide the spinner, regardless of whether the above functions were successful or not - you'll want to stop the spinner if processing is no longer occurring:
spinnerShow();
fetchData()
.then((data) => renderPage(data))
.catch(error)
.finally(spinnerHide);
Promisify
Promisify is an easy way to convert callback functions to a function that returns Promises. Below, we convert a callback-based function called func
to return native Promises.
const promisify = require("es6-promisify");
const fs = require("fs");
const promisifiedFunc = promisify(fs.func);
promisifiedFunc("oranges.txt").then(function (fruit) {
console.log("Fruit received:", fruit);
}).catch(function (error) {
console.error(error);
});
Conclusion
Promises after they're called upon eventually either resolve
or reject
, returning a response based on what the Promise object specifies. By chaining then
and using catch
for errors, we can perform asynchronous functions in an intuitive way and avoid "callback hell". Promises can handle multiple async operations seamlessly, while at the same time providing superior error handling than would be possible with simple callback chaining.
Posted on July 19, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.