# Promises: Resolution, Chaining, and Failure (/docs/programming/async/promises)



## TL;DR [#tldr]

A Promise is not the background operation running on the runtime thread—it is a **claim ticket** (like an order buzzer handed to you at a counter) representing an eventual outcome. Conflating the ticket with the background engine or misunderstanding how chain handlers shape downstream Promises causes catastrophic production outages. In one notable incident, an error handler inside a `.catch()` block merely logged a declined credit card and returned cleanly (`undefined`), silently fulfilling the downstream Promise and prompting the warehouse to dispatch goods to a customer whose payment had completely failed.

> 💡 &#x2A;*Rule of thumb:** Every call to `.then()`, `.catch()`, or `.finally()` creates a brand-new downstream Promise whose fate is determined solely by what its handler returns or throws. When diagnosing an async pipeline, always trace forward through downstream Promises rather than assuming the source Promise mutates.

* **A Promise is a receipt for a future value, not the running task:** Rejecting or abandoning a Promise does not cancel in-flight network sockets, database queries, or OS threads.
* **Resolved does not mean fulfilled:** A Promise is *resolved* when its eventual fate is locked. It can be **pending yet resolved** if it has adopted another still-pending Promise.
* **Chain methods allocate separate downstream Promises:** Returning a plain value fulfills downstream; throwing rejects downstream; returning a Promise/thenable adopts that eventual outcome.
* **Combinators enforce strict contracts:** Choose `Promise.all()` for fail-fast dependencies, `Promise.allSettled()` for complete observation, `Promise.any()` for first successful result, and `Promise.race()` for raw settlement timers.
* **Fatal pitfall (Swallowed Errors):** An error-handling `.catch()` that merely logs an exception without rethrowing returns `undefined`, which silently **fulfills** the downstream Promise. Downstream `.then()` steps—such as order fulfillment or database commits—will execute as if the payment succeeded.

## Start with one small chain [#start-with-one-small-chain]

```js
const p0 = Promise.resolve(10);
const p1 = p0.then((value) => value * 2);
```

Do not imagine `p0` changing from `10` into `20`. `p0` stays fulfilled with `10`; `then()` creates `p1`, and the handler's return value makes `p1` fulfill with `20`.

```text
P0 fulfilled: 10
  ↓ handler returns 20
P1 fulfilled: 20
```

<TermBox term="Downstream Promise">
  A **downstream Promise** is the new Promise returned by a chain method such as `then()`, `catch()`, or `finally()`.

  **Why it matters here:** handlers determine the outcome of the Promise returned by the chain call; they do not rewrite the source Promise.
</TermBox>

## Promise state and resolution [#promise-state-and-resolution]

The basic states are straightforward:

* **pending** — neither fulfilled nor rejected yet;
* **fulfilled** — completed successfully with a value;
* **rejected** — completed unsuccessfully with a reason.

<TermBox term="Settled">
  A Promise is **settled** once it is fulfilled or rejected. A pending Promise is not settled.

  ```text
  settled = fulfilled OR rejected
  ```
</TermBox>

<TermBox term="Resolved">
  **Resolved** means the Promise's eventual fate has been fixed. Resolved is not a fourth mutually exclusive state and is not a synonym for fulfilled.

  A Promise can be **pending and already resolved** when it has been resolved to another still-pending Promise or thenable whose eventual outcome it must follow.
</TermBox>

<AtlasIllustration id="promise-state-machine" />

The simple cases look unsurprising:

```js
Promise.resolve(42);          // resolved and fulfilled
Promise.reject(new Error());  // resolved and rejected
```

The important case is adoption.

<TermBox term="Promise adoption">
  **Promise adoption** means one Promise has been resolved to another Promise or thenable and will follow that value's eventual outcome.

  **Why it matters here:** the adopting Promise can remain pending while the adopted value is pending, even though later attempts to give the adopting Promise a different fate no longer take effect.
</TermBox>

First, notice an identity rule that is easy to miss:

```js
let resolveInner;

const inner = new Promise((resolve) => {
  resolveInner = resolve;
});

const same = Promise.resolve(inner);
console.log(same === inner); // true
```

For a native Promise produced by the same `Promise` constructor, `Promise.resolve(inner)` returns `inner` itself. It does **not** create a second wrapper Promise.

To demonstrate a distinct Promise adopting `inner`, construct a separate Promise and resolve it with `inner`:

```js
const outer = new Promise((resolve) => {
  resolve(inner);
});

console.log(outer === inner); // false
```

While `inner` is still pending:

```text
inner: pending
outer: pending + resolved/adopting inner
```

`outer` is already resolved, but it is not fulfilled yet because `inner` has not settled. Later:

```js
resolveInner(42);
```

produces:

```text
inner: fulfilled with 42
outer: fulfilled with 42
```

This is why “resolved” and “fulfilled” are not synonyms.

## Promise construction: synchronous executor, asynchronous reactions [#promise-construction-synchronous-executor-asynchronous-reactions]

The executor passed to `new Promise(...)` runs synchronously during construction:

```js
const events = [];

new Promise((resolve) => {
  events.push('executor');
  resolve();
});

events.push('after constructor');

console.log(events);
// ['executor', 'after constructor']
```

If the executor throws before either resolving function has already taken effect, the constructor attempts to reject the Promise with that thrown reason. But the first successful call to `resolve` or `reject` fixes the Promise's fate for subsequent resolving-function calls.

That distinction matters for a resolved-but-still-pending Promise:

```js
let resolveInner;

const inner = new Promise((resolve) => {
  resolveInner = resolve;
});

const outer = new Promise((resolve) => {
  resolve(inner);
  throw new Error('does not replace the adopted outcome');
});
```

`resolve(inner)` has already resolved `outer` to `inner`. The later constructor attempt to reject `outer` after the throw has no effect because the resolving functions share a first-resolution guard. If `inner` later fulfills with `42`, `outer` still fulfills with `42`.

The constructor is useful when adapting callback-style APIs:

```js
function readLegacyResource() {
  return new Promise((resolve, reject) => {
    callbackStyleApi((error, value) => {
      if (error) {
        reject(error);
        return;
      }

      resolve(value);
    });
  });
}
```

Do not wrap an API that already returns a Promise unless you need different lifecycle semantics. Prefer `return fetch('/api/user')` over constructing another Promise around `fetch()` merely to forward `resolve` and `reject`.

### Promise reaction handlers do not run inline [#promise-reaction-handlers-do-not-run-inline]

<TermBox term="Promise reaction">
  A **Promise reaction** is the deferred work associated with a Promise handler such as a fulfillment or rejection handler registered by `then()`.

  **Why it matters here:** even when the source Promise is already settled, the handler is scheduled for later reaction processing rather than called inline during the `then()` expression.
</TermBox>

```js
console.log('A');

Promise.resolve().then(() => {
  console.log('promise handler');
});

console.log('B');
```

For a browser, the output is:

```text
A
B
promise handler
```

For the browser scheduling mechanism that places Promise reactions into microtask processing, see [Browser Event Loop: How Tasks, Microtasks, and Rendering Are Scheduled](./how-the-browser-event-loop-works).

## Every chain method creates a downstream Promise [#every-chain-method-creates-a-downstream-promise]

With standard Promise methods, this:

```js
const p1 = p0.then(handleValue);
const p2 = p1.catch(handleError);
const p3 = p2.finally(cleanup);
```

is better pictured as:

```text
P0 --then(handleValue)--> P1 --catch(handleError)--> P2 --finally(cleanup)--> P3
```

`P0`, `P1`, `P2`, and `P3` are separate Promise objects. Each downstream Promise has its **own** outcome.

<AtlasIllustration id="promise-chain-outcomes" />

## Return, throw, adopt [#return-throw-adopt]

This table is the core Promise-chain reasoning tool.

| Handler result                           | Downstream Promise behavior                           |
| ---------------------------------------- | ----------------------------------------------------- |
| returns a plain value                    | fulfills with that value                              |
| returns normally with no value           | fulfills with `undefined`                             |
| throws                                   | rejects with the thrown reason                        |
| returns a fulfilled Promise/thenable     | adopts it and eventually fulfills with that outcome   |
| returns a rejected Promise/thenable      | adopts it and eventually rejects with that outcome    |
| returns a still-pending Promise/thenable | becomes resolved to/adopts it while remaining pending |

### Return a value [#return-a-value]

```js
const p0 = Promise.resolve(10);
const p1 = p0.then((value) => value * 2);
```

`p1` fulfills with `20`.

### Return nothing [#return-nothing]

```js
const p1 = p0.then(() => {
  recordMetric();
});
```

A function that completes normally without an explicit `return` returns `undefined`, so `p1` fulfills with `undefined`. This commonly breaks chains when downstream work expected a value or Promise.

### Throw [#throw]

```js
const p1 = p0.then(() => {
  throw new Error('boom');
});
```

The throw rejects `p1`; it does not mutate `p0`.

### Return another Promise [#return-another-promise]

```js
const p1 = p0.then(() => loadUser());
```

If `loadUser()` returns a Promise, `p1` adopts that Promise's eventual outcome. Normal Promise chains therefore flatten asynchronous dependencies instead of giving application code a useful nested `Promise<Promise<T>>` shape.

### Check yourself: what does this chain settle to? [#check-yourself-what-does-this-chain-settle-to]

Predict the fulfillment value of `p1` before opening the answer.

```js
const p0 = Promise.resolve({ id: 7 });
const p1 = p0.then((user) => {
  loadProfile(user.id); // intentional omission for this exercise
  return user.id;
});
```

Assume `loadProfile()` returns a Promise that fulfills with a profile object.

<details>
  <summary>
    Show the reasoning
  </summary>

  * The handler returns `user.id` (`7`) as a plain value, so `p1` fulfills with `7`.
  * `loadProfile(user.id)` still starts, but its Promise is **not** adopted by `p1` because it was not returned.
  * Downstream code that awaits `p1` therefore continues with `7` while profile loading may still be unfinished—an ordering bug that looks “async” but is not sequenced.
</details>

### Return a thenable [#return-a-thenable]

<TermBox term="Thenable">
  A **thenable** is an object with a callable `then` property. Promise resolution can assimilate these Promise-like objects and follow the outcome they report.

  **Why it matters here:** returning a thenable from a handler can make the downstream Promise adopt it even when the object is not a native Promise.
</TermBox>

```js
const thenable = {
  then(resolve) {
    resolve(42);
  },
};

const p1 = Promise.resolve().then(() => thenable);
```

In application code, prefer real Promises from trustworthy APIs. Thenable assimilation is mainly important so the language can interoperate with Promise-like values.

## Try it: Promise Resolution Lab [#try-it-promise-resolution-lab]

The lab below does **not** execute arbitrary JavaScript. Native Promises do not expose all internal resolution/adoption metadata through a public inspection API, so the lab uses a deterministic teaching model that makes those semantics visible.

<PromiseResolutionLab />

Pay special attention to **Adopt a still-pending promise**. The lab intentionally shows:

```text
P1 state: Pending
P1 resolution: Adopting another promise
P1 adopts: P2
P2 state: Pending
```

That is a concrete resolved-but-not-fulfilled state.

## Error propagation and recovery [#error-propagation-and-recovery]

<AtlasIllustration id="promise-error-propagation" />

When the current outcome does not match a supplied handler, that outcome propagates until a matching handler is reached.

### `catch()` can recover a chain [#catch-can-recover-a-chain]

```js
loadUser()
  .catch(() => ({ name: 'Guest' }))
  .then(renderUser);
```

If `loadUser()` rejects and the `catch()` handler returns the fallback object normally, the Promise returned by `catch()` becomes **fulfilled** with that object. The following fulfillment handler can run normally.

### Logging is not the same as propagating [#logging-is-not-the-same-as-propagating]

```js
loadUser().catch((error) => {
  log(error);
});
```

This rejection handler returns normally with `undefined`, so the Promise returned by `catch()` fulfills with `undefined`. If the error must remain a failure, rethrow it or return a rejected Promise.

### Check yourself: does `catch()` stop later handlers? [#check-yourself-does-catch-stop-later-handlers]

```js
fetchCart()
  .catch((error) => {
    report(error);
    return emptyCart();
  })
  .then((cart) => renderCheckout(cart));
```

If `fetchCart()` rejects and `emptyCart()` returns `{ items: [] }`, does `renderCheckout` run?

<details>
  <summary>
    Show the reasoning
  </summary>

  * Yes. The `catch()` handler recovers by returning a normal cart value, so the Promise returned by `catch()` **fulfills**.
  * The following `then()` therefore runs with that recovered cart.
  * Recovery is intentional here. The dangerous case is logging-only `catch()` that returns `undefined` and still lets a later “success” step run.
</details>

## `finally()` is transparent until cleanup fails or delays [#finally-is-transparent-until-cleanup-fails-or-delays]

`finally()` is primarily for cleanup that should run regardless of fulfillment or rejection.

A normally completing `finally()` callback preserves the original fulfillment value or rejection reason. If it throws or returns a rejected Promise, that cleanup failure becomes the downstream rejection. If it returns a pending Promise, propagation waits for cleanup to settle.

This is why `promise.finally(onFinally)` is **not** equivalent to `promise.then(onFinally, onFinally)`: value/reason propagation semantics differ.

## Branching is not sequencing [#branching-is-not-sequencing]

Calling `then()` multiple times on one source Promise creates separate downstream Promises:

```js
const source = Promise.resolve(10);

const a = source.then((value) => value + 1);
const b = source.then((value) => value * 2);
```

```text
          ┌─ handler A → a fulfilled with 11
source 10 ┤
          └─ handler B → b fulfilled with 20
```

It is not `source → handler A → handler B` unless you actually chain B from A. This distinction connects directly to [Async Waterfalls: Overlap Independent Work Without Breaking Dependencies](./avoiding-sequential-async-waterfalls): chain shape represents dependency shape.

## Promise combinators by intent [#promise-combinators-by-intent]

<TermBox term="Promise combinator">
  A **Promise combinator** is a static Promise API that combines several input values/Promises into one aggregate Promise, such as `Promise.all()`, `Promise.allSettled()`, `Promise.any()`, or `Promise.race()`.

  **Why it matters here:** each combinator has a different success and failure contract. Choose the contract you need rather than treating them as interchangeable ways to “run things in parallel.”
</TermBox>

<AtlasIllustration id="promise-combinators" />

| Need                        | API                    | Key failure behavior                        |
| --------------------------- | ---------------------- | ------------------------------------------- |
| all inputs must fulfill     | `Promise.all()`        | rejects when an input rejects               |
| observe every input outcome | `Promise.allSettled()` | fulfills with per-input result records      |
| first fulfillment wins      | `Promise.any()`        | rejects with `AggregateError` if all reject |
| first settlement wins       | `Promise.race()`       | settles with the first settled input        |

`Promise.all()` is useful when every input matters. Aggregate rejection does not automatically cancel other already-started operations.

`Promise.allSettled()` waits for every input and fulfills with per-input `{ status, value/reason }` records.

`Promise.any()` fulfills with the first fulfillment; if every input rejects, it rejects with an `AggregateError`.

`Promise.race()` settles with the first fulfillment **or rejection**. Timeout-style races still do not automatically cancel the losing operation.

### Empty inputs follow the combinator contract [#empty-inputs-follow-the-combinator-contract]

```text
Promise.all([])        -> already fulfilled with []
Promise.allSettled([]) -> already fulfilled with []
Promise.any([])        -> already rejected with AggregateError
Promise.race([])       -> remains pending
```

Even when an aggregate Promise is already settled, a later `.then(...)` reaction follows normal Promise reaction scheduling. “Already fulfilled” does not mean the reaction handler runs inline.

## Modern Promise APIs in 2026 [#modern-promise-apis-in-2026]

### `Promise.withResolvers()` [#promisewithresolvers]

`Promise.withResolvers()` returns one new Promise together with its resolving functions:

```js
const { promise, resolve, reject } = Promise.withResolvers();
```

It is useful when the lifecycle owner needs those functions outside a constructor callback—for example event, queue, stream, or protocol integration. Keep `resolve` and `reject` near the subsystem that owns the lifecycle; do not turn the API into globally mutable Promise state.

### `Promise.try()` [#promisetry]

ECMAScript 2026 includes `Promise.try()`:

```js
const result = Promise.try(fn, arg1, arg2);
```

It is useful at boundaries where a callback may return a plain value, throw synchronously, or return a Promise/thenable. `Promise.try()` invokes the callback **synchronously**, then resolves or rejects the returned Promise from the callback's completion.

#### `Promise.try(fn)` is not timing-equivalent to `Promise.resolve().then(fn)` [#promisetryfn-is-not-timing-equivalent-to-promiseresolvethenfn]

```js
const events = [];

Promise.try(() => {
  events.push('try callback');
});

Promise.resolve().then(() => {
  events.push('then callback');
});

events.push('sync end');
```

Before the current synchronous JavaScript finishes, the array has already seen:

```text
try callback
sync end
```

The `then()` callback runs later as Promise reaction work.

## Cancellation and operation ownership [#cancellation-and-operation-ownership]

> **A Promise models an eventual result; it does not inherently own or cancel the underlying operation.**

For example, Fetch cancellation belongs to the Fetch operation through `AbortSignal`:

```js
const controller = new AbortController();

const request = fetch('/api/report', {
  signal: controller.signal,
});

controller.abort();
```

Likewise, `Promise.race([request, timeout])` or `Promise.all([a, b, c])` does not automatically cancel losing or remaining operations. If cancellation matters, use the underlying API's cancellation mechanism and define lifecycle ownership.

## How `async` / `await` connects [#how-async--await-connects]

`async` / `await` is syntax built on Promise semantics, not a separate asynchronous model. An `async` function returns a Promise; `await` makes the surrounding async-function continuation depend on an eventual outcome. Fulfillment resumes with a value; rejection behaves like a throw at the `await` expression.

The syntax can still create an accidental sequential waterfall when independent operations are awaited one after another.

## Production mistakes to avoid [#production-mistakes-to-avoid]

### Production scenario: The "swallowed error" phantom confirmation [#production-scenario-the-swallowed-error-phantom-confirmation]

A backend payment service coordinates order settlement using a Promise chain:

```ts
function processOrderPayment(orderId: string) {
  return chargeCustomer(orderId)
    .catch((err) => {
      // Developer logs the error but forgets to rethrow:
      logger.error('Failed to charge card', { orderId, err });
    })
    .then(() => {
      // This step STILL RUNS because the .catch() above returned undefined (fulfilled)!
      return markOrderAsPaidAndDispatch(orderId);
    });
}
```

* **Impact:** When a customer's card is declined or has insufficient funds, `chargeCustomer` rejects. However, `.catch()` merely logs the error and returns cleanly (`return undefined`), transforming the downstream Promise into a **fulfilled** state! The system promptly calls `markOrderAsPaidAndDispatch(orderId)`, dispatching goods to the customer without collecting payment!
* **Root cause:** The developer treated `.catch()` as a passive error listener rather than a **recovery handler**. Unless an error is explicitly rethrown, downstream consumers treat the branch as successfully handled and recovered.
* **Correct pattern:** When logging or observing a failure without recovering, always rethrow the error:
  ```ts
  return chargeCustomer(orderId)
    .catch((err) => {
      logger.error('Failed to charge card', { orderId, err });
      throw err; // Propagate the rejection downstream
    })
    .then(() => markOrderAsPaidAndDispatch(orderId));
  ```

### Wrapping Promise APIs unnecessarily [#wrapping-promise-apis-unnecessarily]

Avoid constructing another Promise merely to forward an existing Promise API's `resolve` and `reject`. Return or transform the existing Promise unless you need genuinely different lifecycle semantics.

### Forgetting to return asynchronous work from a handler [#forgetting-to-return-asynchronous-work-from-a-handler]

```js
loadUser()
  .then((user) => {
    saveUser(user); // forgot return
  })
  .then(() => {
    showToast('Saved');
  });
```

* **Impact:** The toast can appear while `saveUser` is still in flight—or after it later rejects—so the product reports success before durability is known.
* **Root cause:** The first handler returns `undefined` immediately, so the downstream Promise fulfills without adopting `saveUser`'s Promise.
* **Correct pattern:** Return the asynchronous work you intend to sequence:

```js
loadUser()
  .then((user) => saveUser(user))
  .then(() => {
    showToast('Saved');
  });
```

### Accidentally swallowing a rejection [#accidentally-swallowing-a-rejection]

A `catch()` handler that only logs and then returns normally converts the downstream outcome into fulfillment with `undefined`. Decide whether recovery is intentional.

### Assuming rejection cancels work [#assuming-rejection-cancels-work]

It does not. Tie cancellation to the actual operation API.

### Creating unbounded work because aggregation looks simple [#creating-unbounded-work-because-aggregation-looks-simple]

```js
await Promise.all(items.map(processItem));
```

calls `processItem` for every element before `Promise.all()` waits for the aggregate. If those calls immediately start resource-consuming work, this can overwhelm databases, APIs, file descriptors, memory, or rate limits. Promise aggregation and concurrency limits solve different problems.

### Confusing unhandled rejection policy with Promise semantics [#confusing-unhandled-rejection-policy-with-promise-semantics]

A rejection is a Promise outcome. What a browser, Node.js process, test runner, or framework logs, reports, or terminates because a rejection is unhandled is runtime policy around that outcome.

## Exercise [#exercise]

Predict the settlement state and values of `p0` through `p4`:

```js
let settleInner;

const inner = new Promise((resolve) => {
  settleInner = resolve;
});

const p0 = Promise.resolve(2);
const p1 = p0.then((value) => value * 3);
const p2 = p1.then(() => {
  throw new Error('boom');
});
const p3 = p2.catch(() => inner);
const p4 = p3.then((value) => value + 1);

settleInner(10);
```

<details>
  <summary>
    Show the reasoning
  </summary>

  * `p0` fulfills with `2`.
  * `p1` fulfills with `6` (`2 * 3`).
  * `p2` rejects with `Error('boom')` due to the synchronous throw inside the handler.
  * When `p2` rejects, the `catch()` handler catches it and returns the still-pending `inner` Promise. At this point, `p3` is &#x2A;*pending but resolved/adopting `inner`**.
  * Once `settleInner(10)` is invoked, `inner` fulfills with `10`. Consequently, `p3` fulfills with `10`.
  * Finally, `p4` receives `10`, adds `1`, and fulfills with `11`.
</details>

## Agent rule [#agent-rule]

> **For standard Promise chaining, treat each `then()`, `catch()`, and `finally()` call as producing a separate downstream Promise. Determine that downstream outcome from the relevant handler: returning a value fulfills it, throwing rejects it, and returning a Promise/thenable makes it adopt that outcome. Do not equate resolved with fulfilled, do not assume rejection cancels underlying work, and choose combinators according to the success/failure contract you need.**

When changing Promise-heavy code, verify against this checklist:

* [ ] **Downstream Promise awareness:** Does each `.then()`, `.catch()`, or `.finally()` call explicitly account for returning a new downstream Promise?
* [ ] **Return vs Throw contract:** Does every handler explicitly return a value/Promise or throw an error, rather than accidentally falling through to `undefined`?
* [ ] **Error recovery vs swallow:** Is each `.catch()` intentional about either recovering with fallback data or re-throwing to preserve failure signals?
* [ ] **Cleanup transparency:** Does `.finally()` perform side-effect cleanup without attempting to transform fulfillment values?
* [ ] **Combinator semantics:** Does the selected combinator (`all`, `allSettled`, `race`, `any`) match the required failure mode (fail-fast vs complete observation)?
* [ ] **Cancellation decoupling:** Are cancellation needs attached to `AbortSignal` rather than assuming a rejected Promise cancels pending network/disk I/O?
* [ ] **Concurrency boundaries:** Is mass asynchronous fan-out bounded (e.g. queue/pool) rather than blindly launching unbounded `Promise.all(items.map(...))`?

## Related lessons [#related-lessons]

* [Async Waterfalls: Overlap Independent Work Without Breaking Dependencies](./avoiding-sequential-async-waterfalls)
* [Browser Event Loop: How Tasks, Microtasks, and Rendering Are Scheduled](./how-the-browser-event-loop-works)
* **Async / Await** — future lesson on expressing Promise dependencies clearly.
* **Cancellation** — future lesson on AbortSignal, ownership, propagation, and cleanup.
* **Bounded Concurrency** — future lesson on controlling resource pressure independently of Promise aggregation.

## Primary sources [#primary-sources]

This lesson was last verified on **2026-09-10** and is classified as **evolving** with a 180-day review target.

* [ECMAScript 2026 — Promise Objects](https://tc39.es/ecma262/2026/multipage/control-abstraction-objects.html#sec-promise-objects) — normative Promise states, resolution, constructor behavior, combinators, `Promise.try()`, `Promise.withResolvers()`, and prototype methods.
* [ECMAScript 2026 — `Promise.resolve()` / `PromiseResolve`](https://tc39.es/ecma262/2026/multipage/control-abstraction-objects.html#sec-promise.resolve) — normative identity behavior for same-constructor Promises and creation of a new resolving Promise otherwise.
* [MDN — Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) — practical terminology and developer-facing reference.
* [MDN — Promise constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise) — executor timing and first-resolution behavior.
* [MDN — Promise.prototype.then()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) — chaining and downstream-Promise behavior.
* [MDN — Promise.prototype.finally()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally) — cleanup transparency and exceptional outcomes.
* [MDN — Promise.all()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all), [allSettled()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled), [any()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any), and [race()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race) — aggregation contracts and empty-input behavior.
* [MDN — Promise.withResolvers()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers) — standardized construction primitive and compatibility context.
* [MDN — Promise.try()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/try) — standardized callback normalization and synchronous callback invocation.
