Implement a Promise Class in JavaScript with then Chaining, catch and finally

Read the full interview experience this question came from →

Quick Overview

Implement a promise class from scratch in plain JavaScript: an executor with resolve and reject, then with chaining and adoption of returned promises, plus catch and finally. Tests state machines, asynchronous callback scheduling and error propagation through promise chains.

Implement a Promise Class in JavaScript with then Chaining, catch and finally

Company: MongoDB

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Onsite

Implement, in plain JavaScript and without using the built-in `Promise`, a class `MyPromise` that provides the core functionality of a promise. The report does not list the required methods; the parts below cover the standard core, with `then` as the central piece, so confirm the scope with the interviewer. ```js const p = new MyPromise((resolve, reject) => { setTimeout(() => resolve(21), 10); }); p.then((x) => x * 2) .then((x) => console.log(x)); // logs 42 ``` ### Clarifying Questions - Which methods are required: the constructor and `then` only, or also `catch`, `finally` and static helpers such as `resolve`, `reject` and `all`? - Must callbacks run asynchronously, as they do with native promises, and may `queueMicrotask` or `setTimeout` be used to schedule them? - Must the class interoperate with other promise-like objects (anything with a `then` method), including native promises? - How will the implementation be checked: with my own examples, or with a provided test suite? ### Part 1 — States and the executor Implement the constructor. It receives an executor function and calls it immediately with a `resolve` and a `reject` function. The promise starts pending and settles at most once, either fulfilled with a value or rejected with a reason. If the executor throws, the promise is rejected with the thrown error. ```hint Settle once Consider what should happen when the executor calls `resolve` twice, calls `reject` after `resolve`, or throws after it has already resolved. ``` #### What This Part Should Cover - The three states and the rule that the first settlement wins - Catching an exception thrown by the executor - Storing callbacks that are registered while the promise is still pending ### Part 2 — `then` and chaining Implement `then(onFulfilled, onRejected)`. It registers the callbacks and returns a new promise that settles according to whichever callback runs. It must work whether `then` is called before or after the promise settles, and chained calls must pass values and errors along. ```hint What the next promise waits for Think about what the returned promise should do when the callback returns a plain value, throws, or returns another promise. ``` ```hint A missing callback Ask what `p.then(undefined, onRejected)` must pass along when `p` is fulfilled, and what `p.then(onFulfilled)` must pass along when `p` is rejected. ``` #### What This Part Should Cover - A new promise returned by every call, settled from the callback's return value or thrown error - Pass-through of the value or reason when a callback is missing or is not a function - Asynchronous invocation of callbacks, in the order they were registered - Adoption of a returned promise or thenable, and rejection when a promise is resolved with itself ### Part 3 — `catch` and `finally` Add `catch(onRejected)` and `finally(onFinally)` on top of `then`. ```hint Build on then Consider how little new code each method needs when it is expressed through `then`, and what `finally` must pass through unchanged. ``` #### What This Part Should Cover - `catch` as a special case of `then` - `finally` running its callback for both outcomes without changing the value or reason, unless the callback throws or returns a rejected promise ### What a Strong Answer Covers - A correct state machine with single settlement and handling of executor errors - A `then` that returns a new promise and handles plain values, thrown errors, returned promises and thenables - Asynchronous callback execution on the microtask queue, preserving registration order - Pass-through for missing handlers, so that values and errors flow down a chain - Small runnable checks for ordering, chaining and error propagation ### Follow-up Questions - Implement `MyPromise.all` and explain the order of its results and when it rejects. - What output order do you get if callbacks are scheduled with `setTimeout` instead of `queueMicrotask`, for code that mixes timers and promises? - Why must `resolve` check whether its argument is a thenable, and what goes wrong if it does not? - How would you report a rejection that nobody handles?

Overview: Implement a promise class from scratch in plain JavaScript: an executor with resolve and reject, then with chaining and adoption of returned promises, plus catch and finally. Tests state machines, asynchronous callback scheduling and error propagation through promise chains.

Read the full MongoDB Software Engineer interview experience this question came from

MongoDB logo
MongoDB
Oct 31, 2026
mediumSoftware EngineerOnsiteSoftware Engineering Fundamentals
1
0

Implement, in plain JavaScript and without using the built-in Promise, a class MyPromise that provides the core functionality of a promise. The report does not list the required methods; the parts below cover the standard core, with then as the central piece, so confirm the scope with the interviewer.

const p = new MyPromise((resolve, reject) => {
  setTimeout(() => resolve(21), 10);
});

p.then((x) => x * 2)
  .then((x) => console.log(x));   // logs 42

Clarifying Questions Guidance

  • Which methods are required: the constructor and then only, or also catch , finally and static helpers such as resolve , reject and all ?
  • Must callbacks run asynchronously, as they do with native promises, and may queueMicrotask or setTimeout be used to schedule them?
  • Must the class interoperate with other promise-like objects (anything with a then method), including native promises?
  • How will the implementation be checked: with my own examples, or with a provided test suite?

Part 1 — States and the executor

Implement the constructor. It receives an executor function and calls it immediately with a resolve and a reject function. The promise starts pending and settles at most once, either fulfilled with a value or rejected with a reason. If the executor throws, the promise is rejected with the thrown error.

What This Part Should Cover Guidance

  • The three states and the rule that the first settlement wins
  • Catching an exception thrown by the executor
  • Storing callbacks that are registered while the promise is still pending

Part 2 — then and chaining

Implement then(onFulfilled, onRejected). It registers the callbacks and returns a new promise that settles according to whichever callback runs. It must work whether then is called before or after the promise settles, and chained calls must pass values and errors along.

What This Part Should Cover Guidance

  • A new promise returned by every call, settled from the callback's return value or thrown error
  • Pass-through of the value or reason when a callback is missing or is not a function
  • Asynchronous invocation of callbacks, in the order they were registered
  • Adoption of a returned promise or thenable, and rejection when a promise is resolved with itself

Part 3 — catch and finally

Add catch(onRejected) and finally(onFinally) on top of then.

What This Part Should Cover Guidance

  • catch as a special case of then
  • finally running its callback for both outcomes without changing the value or reason, unless the callback throws or returns a rejected promise

What a Strong Answer Covers Guidance

  • A correct state machine with single settlement and handling of executor errors
  • A then that returns a new promise and handles plain values, thrown errors, returned promises and thenables
  • Asynchronous callback execution on the microtask queue, preserving registration order
  • Pass-through for missing handlers, so that values and errors flow down a chain
  • Small runnable checks for ordering, chaining and error propagation

Follow-up Questions Guidance

  • Implement MyPromise.all and explain the order of its results and when it rejects.
  • What output order do you get if callbacks are scheduled with setTimeout instead of queueMicrotask , for code that mixes timers and promises?
  • Why must resolve check whether its argument is a thenable, and what goes wrong if it does not?
  • How would you report a rejection that nobody handles?