Promises are a powerful tool for dealing with asynchronous code in JavaScript, allowing you to avoid the callback hell. In this blog post, we will explore how to use promises, including creating promises, consuming promises, chaining promises, handling errors, and orchestrating promises.

Introduction to Promises

A promise is a proxy for a value that will eventually become available. It provides a way to work with asynchronous code without writing too many callbacks. Promises were standardized and introduced in ES2015, and now they have been superseded in ES2017 by async functions.

How Promises Work, in Brief

Once a promise is called, it starts in a pending state. While the promise does its own processing, the caller function continues its execution, waiting for the promise to either return in a resolved state or a rejected state. This allows the function to continue its execution without blocking.

Which JS API Use Promises?

Promises are widely used in modern JavaScript in addition to your own code and library code. They are used by standard Web APIs such as the Battery API, Fetch API, and Service Workers. It is unlikely that you will find yourself not using promises in modern JavaScript.

Creating a Promise

To create a promise, use the Promise constructor provided by the Promise API. Initialize it using new Promise() and pass a callback function with two parameters, resolve and reject. Inside the callback function, you can check certain conditions and call resolve or reject accordingly.

Consuming a Promise

To consume or use a promise, you need to call methods like then and catch on the promise instance. The then method is used to handle the resolved state of the promise and the catch method is used to handle the rejected state. These methods take callback functions as parameters.

Chaining Promises

Promises can be chained together by returning a promise from within another promise’s then callback. This creates a chain of promises, allowing you to execute a sequence of asynchronous operations. A great example of chaining promises is the Fetch API, which allows you to get a resource and queue a chain of promises to execute when the resource is fetched.

Handling Errors

When an error occurs in a promise or in any chained promises, the control goes to the nearest catch statement in the promise chain. You can use the catch method to handle errors and perform error handling logic. If an error is thrown inside a catch block, you can chain another catch to handle it, and so on.

Orchestrating Promises

Promise.all()

If you need to synchronize different promises and execute something when they are all resolved, you can use the Promise.all() method. It takes an array of promises as input and returns a new promise that resolves when all the promises in the array have been resolved. You can then use then to handle the resolved results or catch to handle any errors.

Promise.race()

Promise.race() runs as soon as one of the promises you pass to it resolves, and it runs the attached callback once with the result of the first resolved promise. This can be useful when you only care about the result of the first promise that resolves. You can use then to handle the result or catch to handle any errors.

Common Errors

Uncaught TypeError: undefined is not a promise

If you encounter the Uncaught TypeError: undefined is not a promise error, make sure you use new Promise() instead of just Promise(). This error occurs when using Promise() without the new keyword to create a new promise instance.