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