One thing that we haven't touched on so far, which is interesting to consider, is the fact that promises can be chained. This is especially useful to avoid code duplication.
In our Fetch example, we have called an API, checked the status code, retrieved the body and parsed it as JSON. Here's another way to implement the same functionality but in a more reusable manner:
function checkResponseStatus(response: Response): Promise<Response> { if (response.status >= 200 && response.status < 300) { return Promise.resolve(response); } else { return Promise.reject(new Error(response.statusText)); } }
function toJSON(response: Response): Promise<Response> { return response.json(); }
const responsePromise: Promise<Response> = fetch('https://api.worldbank.org/v2/countries?format=json'); responsePromise .then(checkResponseStatus) .then(toJSON) .then((jsonContent: unknown) => { console.log("Response content: ", jsonContent); }) .catch((error: unknown) => { console.error("An error occurred while fetching the data: ",
error); });
This time, we've defined a function whose sole responsibility is to check whether the given response has a valid status; it uses the Promise.resolve(...) and Promise.reject(...) functions to return a promise and a result.
We've also defined a simple function taking care of the .json() call, which also returns a promise. Our code handling the Promise response is now more concise and much clearer. Also, the functions that we have defined can be reused!
Chaining promises is really useful and will make your life easier. But we can do even better by leveraging async and await. Let's look at those next.