Omeji Mustapha
3 min readJul 9, 2024

Promises in Asynchronous JavaScript

In JavaScript, asynchronous operations are crucial for handling tasks such as network requests, file reading, and other I/O operations without blocking the main execution thread. Promises are a modern and powerful way to manage asynchronous operations, providing a more readable and manageable approach compared to traditional callback-based methods.

What is a Promise?

A promise is an object representing the eventual completion or failure of an asynchronous operation that represents success and failure.

A promise is an asynchronous action that may complete at some point and produce a value. It is able to notify anyone who is interested when its value is available. The easiest way to create a promise is by calling Promise. resolve(). This function ensures that the value you give it is wrapped in a promise. If it’s already a promise, it is simply returned—otherwise, you get a new promise that immediately finishes with your value as its result.



It is useful to think of promises as a device to move values into an asynchronous reality. A normal value is simply there. A promised value is a value that might already be there or might appear at some point in the future. Computations defined in terms of promises act on such wrapped values and are executed asynchronously as the values become available.

Promises are new in JavaScript

Promises have three states:

  1. Pending: The initial state, neither fulfilled nor rejected.
  2. Fulfilled: The operation completed successfully.
  3. Rejected: The operation failed.

Creating a Promise

A Promise is created using the Promise constructor, which takes a single function as an argument. This function, known as the executor, is executed immediately by the Promise implementation, passing two functions: resolve and reject.

Example

  1. Creating a Rainbow using promise
//creating a Rainbow using promises
const delayedColorChange = (color, delay) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
document.body.style.backgroundColor = color
resolve()
}, delay)
})
}
delayedColorChange('Red', 1000)
.then(() => delayedColorChange('Orange', 1000))
.then(() => delayedColorChange('Yellow', 1000))
.then(() => delayedColorChange('Green', 1000))
.then(() => delayedColorChange('Blue', 1000))
.then(() => delayedColorChange('Indigo', 1000))
.then(() => delayedColorChange('Violet', 1000))


Code Explanation: Overall Execution Flow

The background color changes to red after 1 second.After changing to red, it waits another second and changes to orange.This process repeats for yellow, green, blue, indigo, and violet, each with a 1-second delay between changes.

By chaining the Promises, this code ensures that each color change occurs only after the previous one has completed, resulting in a smooth, sequential transition through the colors.

Advantages of Promises Over Callback Hell

Here are some of the advantages of JavaScript Promises over callback hell

  1. Error Handling: With Promises, error handling is more straightforward and centralized. Instead of handling errors at each level of nested callbacks, you can use a single .catch() at the end of your chain to handle any errors that occur in any of the preceding Promises.

Chaining and Composition: Promises support chaining, allowing you to chain multiple asynchronous operations in a clear and structured manner. This makes it easier to compose complex asynchronous workflows without the confusion of nested callbacks.

See an example of callback hell in the snippet below 👇

setTimeout(() => {
console.log('Red');
setTimeout(() => {
console.log('Orange');
setTimeout(() => {
console.log('Yellow');
setTimeout(() => {
console.log('Green');
setTimeout(() => {
console.log('Blue');
setTimeout(() => {
console.log('Indigo');
setTimeout(() => {
console.log('Violet');
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);

In conclusion, Promises in JavaScript offer a robust way to handle asynchronous operations, providing a clear and structured approach to managing multiple asynchronous tasks. By utilizing Promise.all(), Promise.race(), and Promise.allSettled(), developers can handle complex scenarios involving multiple asynchronous operations, leading to cleaner, more maintainable code. As JavaScript continues to evolve, Promises remain a fundamental aspect of its asynchronous programming capabilities, forming the basis for modern features like async and await.