Search Knowledge

© 2026 LIBREUNI PROJECT

Modern JavaScript & Ecosystem / Asynchronous JavaScript

Promises and Asynchronous Orchestration

The Death of the Callback

Before ECMAScript 2015, asynchronous operations were handled using callbacks. While functional, this led to “Callback Hell”—deeply nested, unreadable code that made error handling extremely difficult. Promises were introduced to provide a cleaner, more robust way to manage asynchronous operations by representing a value that may be available now, in the future, or never.

The Promise Lifecycle

A Promise is an object that exists in one of three mutually exclusive states:

  1. Pending: Initial state, neither fulfilled nor rejected.
  2. Fulfilled: The operation completed successfully, and the promise has a value.
  3. Rejected: The operation failed, and the promise has a reason (an error).

Once a promise is either fulfilled or rejected, it is settled. A settled promise cannot change its state or value.

Creating and Consuming Promises

A promise is created using the Promise constructor, which takes an “executor” function. This function receives two arguments: resolve and reject.

javascript
1const fetchData = new Promise((resolve, reject) => {
2 const success = true;
3 if (success) {
4 resolve({ id: 1, data: "Orbit Telemetry" });
5 } else {
6 reject("Connection Failure");
7 }
8});
9 
10fetchData
11 .then(data => console.log("Received:", data))
12 .catch(err => console.error("Error:", err))
13 .finally(() => console.log("Request Complete."));

Promise Chaining

The true power of Promises lies in their ability to be chained. Every call to .then() returns a new promise, allowing you to sequence multiple asynchronous operations without nesting.

If a .then() handler returns a value, the new promise is fulfilled with that value. If it returns another promise, the new promise “waits” for that promise to resolve.

fetchUser(id)
  .then(user => fetchOrders(user.id)) // returns a promise
  .then(orders => processPayment(orders[0])) // returns another promise
  .then(receipt => console.log(receipt))
  .catch(handleAnyError); // One catch handles the entire chain

Parallelism and Coordination

JavaScript provides static methods on the Promise class to handle multiple concurrent operations:

  • Promise.all([p1, p2, ...]): Waits for all promises to fulfill. If any single promise rejects, the entire aggregate promise rejects immediately.
  • Promise.allSettled([p1, p2, ...]): Waits for all promises to settle, regardless of outcome.
  • Promise.race([p1, p2, ...]): Resolves or rejects as soon as the first promise in the array settles.
  • Promise.any([p1, p2, ...]): Resolves as soon as the first promise fulfills. It only rejects if all promises reject.

Promise.all behavior

const p1 = Promise.resolve(1);
const p2 = Promise.reject("Error");
const p3 = Promise.resolve(3);

Promise.([p1, p2, p3])
  .catch(e => console.log(e)); // Logs "Error"

Error Propagation

Unlike callbacks, where errors must be manually passed up the chain, Promises naturally propagate errors. A rejection in any link of a .then() chain will skip subsequent .then() handlers and jump directly to the nearest .catch(). This mimics the behavior of try/catch in synchronous code.