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.finally
from scratch. - Explain the behavior of
Promise.finally
with both resolved and rejected promises. - Discuss how
Promise.finally
can 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
finally
method if it is not already present. - Callback Execution: Use the
then
method to execute theonFinally
callback when the promise is resolved or rejected. - Chaining: Ensure the original promise’s value or reason is passed through after the
onFinally
callback 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
finally
method 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
finally
method ensures that "Operation complete" is logged after the promise settles, regardless of the outcome.
Performance Considerations
When using Promise.finally
, consider:
- Execution Time: The
finally
callback is executed as soon as the promise is settled, which ensures timely cleanup. - Side Effects: Ensure that the
finally
callback 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
finally
method to pass the context ('resolved'
or'rejected'
) to theonFinally
callback.