Q23: Implement a polyfill for Promise.prototype.finally().
Answer
Promise.prototype.finally() registers a callback that runs when a promise settles, whether it fulfills or rejects. Normally, the original value or rejection reason is preserved unless the callback itself throws or returns a rejected promise.
if (!Promise.prototype.myFinally) {
Promise.prototype.myFinally = function (callback) {
const PromiseConstructor = this.constructor;
return this.then(
value => PromiseConstructor.resolve(
typeof callback === "function" ? callback() : callback
).then(() => value),
reason => PromiseConstructor.resolve(
typeof callback === "function" ? callback() : callback
).then(() => {
throw reason;
})
);
};
}
Promise.resolve("success")
.myFinally(() => console.log("Cleanup"))
.then(console.log);
// Cleanup
// success
Explanation
The fulfillment handler waits for the cleanup callback and then returns the original fulfillment value. The rejection handler waits for cleanup and then throws the original rejection reason.
If the callback throws or returns a rejected promise, the resulting promise rejects with that new error. Native finally() also has specific handling for non-callable callbacks and promise subclass constructors.
Key Takeaways
- Finally runs after either fulfillment or rejection.
- Successful cleanup preserves the original outcome; failed cleanup can replace it.