Implementing Promise.finally in JavaScript
What is Promise.finally?
Promise.finally is a method that allows you to specify a callback that will be executed when the promise is settled (either resolved or rejected). The callback does not receive any arguments and the promise’s value or reason will be passed through unchanged.
Real Interview Insights
Interviewers might ask you to:
- Implement
Promise.finallyfrom scratch. - Explain the behavior of
Promise.finallywith both resolved and rejected promises. - Discuss how
Promise.finallycan be used for cleanup tasks in asynchronous operations.
Implementing Promise.finally
Here’s a basic implementation of Promise.finally:
if (!Promise.prototype.finally) {
Promise.prototype.finally = function(onFinally) {
const P = this.constructor;
return this.then(
value => P.resolve(onFinally()).then(() => value),
reason => P.resolve(onFinally()).then(() => { throw reason; })
);
};
}Explanation:
- Check for Native Support: Only add the
finallymethod if it is not already present. - Callback Execution: Use the
thenmethod to execute theonFinallycallback when the promise is resolved or rejected. - Chaining: Ensure the original promise’s value or reason is passed through after the
onFinallycallback is executed.
Practical Example
Consider an example with both resolved and rejected promises:
const promise1 = Promise.resolve('Success');
const promise2 = Promise.reject('Error');
promise1.finally(() => console.log('Cleanup after success'))
.then(result => console.log(result)) // Output: 'Cleanup after success', 'Success'
.catch(error => console.error(error));
promise2.finally(() => console.log('Cleanup after error'))
.then(result => console.log(result))
.catch(error => console.error(error)); // Output: 'Cleanup after error', 'Error'In this example:
- The
finallymethod runs the cleanup code regardless of whether the promise was resolved or rejected.
Advanced Use Case: Logging and Cleanup
Promise.finally is particularly useful for logging and cleanup operations:
function fetchData() {
return new Promise((resolve, reject) => {
// Simulate an asynchronous operation
setTimeout(() => resolve('Fetched data'), 1000);
});
}
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error))
.finally(() => console.log('Operation complete'));In this scenario:
- The
finallymethod ensures that "Operation complete" is logged after the promise settles, regardless of the outcome.
Performance Considerations
When using Promise.finally, consider:
- Execution Time: The
finallycallback is executed as soon as the promise is settled, which ensures timely cleanup. - Side Effects: Ensure that the
finallycallback does not have side effects that affect the promise chain.
Coding Challenge: Extending Promise.finally with Context
Challenge: Enhance the Promise.finally implementation to pass context information to the onFinally callback.
if (!Promise.prototype.finally) {
Promise.prototype.finally = function(onFinally) {
const P = this.constructor;
return this.then(
value => P.resolve(onFinally('resolved')).then(() => value),
reason => P.resolve(onFinally('rejected')).then(() => { throw reason; })
);
};
}
// Example usage with context
const promise = Promise.resolve('Success');
promise.finally((context) => console.log(`Cleanup after ${context}`))
.then(result => console.log(result))
.catch(error => console.error(error)); // Output: 'Cleanup after resolved', 'Success'In this challenge:
- Modify the
finallymethod to pass the context ('resolved'or'rejected') to theonFinallycallback.