Implementing a Promise/A+ Compliant Promise in JavaScript
This challenge asks you to implement a robust and fully compliant Promise/A+ specification in JavaScript. Promises are a fundamental part of asynchronous programming, and understanding their underlying implementation provides valuable insight into how asynchronous operations are managed in JavaScript. Successfully completing this challenge will demonstrate a deep understanding of asynchronous JavaScript and the Promise/A+ standard.
Problem Description
Your task is to create a Promise class that adheres to the Promise/A+ specification. This includes implementing all the required methods and behaviors outlined in the specification. The Promise class should handle asynchronous operations, resolve or reject based on the outcome of those operations, and properly chain and manage callbacks. You must implement the core functionality of a Promise, including resolving, rejecting, chaining, and handling errors.
Key Requirements:
new Promise(executor): The constructor should accept anexecutorfunction. Theexecutorfunction should receive two arguments:resolveandreject, which are functions used to signal the fulfillment or rejection of the promise, respectively.then(onFulfilled, onRejected): This method should be called when the promise is resolved or rejected. It should return a new promise. IfonFulfilledis provided, it should be called with the resolved value. IfonRejectedis provided, it should be called with the rejected reason. If neither is provided, the value/reason should be passed to the nextthencall. It must handle both synchronous and asynchronousonFulfilledandonRejectedcallbacks correctly.catch(onRejected): This method is equivalent tothen(null, onRejected). It should return a new promise.finally(onFinally): This method should be called regardless of whether the promise is resolved or rejected. It should return a new promise. TheonFinallycallback should not receive the resolved value or rejection reason. It should not prevent the promise from resolving or rejecting.delete: (Optional, but highly recommended for full compliance) Thedeletemethod should remove a rejected handler from the promise's internal queue. This is important for preventing memory leaks.- Error Handling: Uncaught rejections should be handled appropriately (e.g., logged to the console).
- Chaining: Promises should be chainable. Multiple
thencalls should be executed sequentially. - Asynchronous Execution: Callbacks should be executed asynchronously, using
setTimeoutorsetImmediateto avoid blocking the main thread. - Multiple thens: Multiple
thencalls should be queued and executed in order. - Rejection Propagation: Rejections should propagate down the chain until a handler is found.
- Promise Status: The promise should maintain a consistent state (pending, fulfilled, rejected).
Expected Behavior:
- A new promise should be created when
then,catch, orfinallyis called. - Callbacks should be executed asynchronously.
- Rejections should propagate down the chain.
- The promise should transition between states correctly.
- The
executorfunction should be called immediately upon promise creation.
Edge Cases to Consider:
executorthrowing an error.onFulfilledoronRejectedthrowing an error.- Chaining multiple promises.
- Resolving/rejecting a promise with another promise.
- Resolving/rejecting a promise with
nullorundefined. - Multiple
thencalls. - Promises resolving/rejecting after already resolved/rejected.
Examples
Example 1:
Input:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Success!');
}, 100);
});
promise.then(value => {
console.log(value); // Output: Success!
});
Output: Success!
Explanation: The promise resolves after 100ms, and the then callback is executed with the resolved value.
Example 2:
Input:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Error!');
}, 100);
});
promise.catch(error => {
console.log(error); // Output: Error!
});
Output: Error!
Explanation: The promise rejects after 100ms, and the catch callback is executed with the rejection reason.
Example 3:
Input:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('First');
}, 50);
});
promise.then(value => {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Second');
}, 50);
});
}).then(value => {
console.log(value); // Output: Second
});
Output: Second
Explanation: Demonstrates promise chaining with an asynchronous callback in the first then.
Constraints
- Time Complexity: The
then,catch, andfinallymethods should have a time complexity of O(1). - Space Complexity: The space complexity should be minimized to avoid unnecessary memory usage.
- Browser Compatibility: The implementation should be compatible with modern browsers (ES6+).
- Asynchronous Execution: Callbacks must be executed asynchronously. Using
setTimeout(..., 0)orsetImmediate()is required. Directly calling the callback will result in failure. - No External Libraries: You are not allowed to use any external libraries.
Notes
- The Promise/A+ specification is available online: https://promisesaplus.github.io/promises-spec/ Refer to it for detailed requirements.
- Consider using a queue to manage callbacks.
- Pay close attention to the asynchronous execution of callbacks.
- Thoroughly test your implementation with various scenarios, including edge cases.
- The
deletemethod is optional but demonstrates a deeper understanding of the specification and helps prevent memory leaks. It's highly recommended. - Focus on correctness and adherence to the specification. Performance is secondary, but strive for reasonable efficiency.