Promise.prototype.finally()
Basic Concept of Promise.prototype.finally()
ECMAScript 2018 (ES9) introduced the Promise.prototype.finally()
method, which allows a specified callback function to be executed regardless of whether the Promise succeeds or fails. This method addresses the previous issue of having to repeat the same code in both then()
and catch()
.
The finally()
method takes a callback function as an argument. This callback does not accept any parameters because it does not care whether the Promise's final state is fulfilled or rejected.
const promise = new Promise((resolve, reject) => {
// Asynchronous operation
});
promise
.then(result => { /* Handle success */ })
.catch(error => { /* Handle failure */ })
.finally(() => { /* Executes whether the Promise succeeds or fails */ });
Behavioral Characteristics of finally()
The finally()
method has several important behavioral characteristics worth noting:
- The callback function does not receive any parameters because it does not care about the Promise's final state.
- It returns a new Promise with the same value as the original Promise.
- If the callback function throws an error or returns a rejected Promise, the error will be propagated.
- In a Promise chain,
finally()
does not alter the original Promise's value.
Promise.resolve(2)
.then(x => x * 2)
.finally(() => { console.log('Calculation complete') })
.then(x => console.log(x)); // Output: Calculation complete followed by 4
Promise.reject(new Error('Failure'))
.catch(e => { console.log(e.message); return 'Recovery value' })
.finally(() => console.log('Cleanup work'))
.then(x => console.log(x)); // Output: Failure, Cleanup work, Recovery value
Differences from then/catch
finally()
differs from then()
and catch()
in several key ways:
- Parameter Handling:
then()
andcatch()
callbacks receive the Promise's result or error, whilefinally()
does not receive any parameters. - Impact on Return Value: The return value of
finally()
's callback does not affect the Promise chain unless an error is thrown. - Execution Timing:
finally()
ensures execution after the Promise completes, regardless of success or failure.
// then/catch require repeating code
somePromise
.then(result => {
doSomething(result);
cleanup();
})
.catch(error => {
handleError(error);
cleanup();
});
// Using finally is more concise
somePromise
.then(result => doSomething(result))
.catch(error => handleError(error))
.finally(() => cleanup());
Practical Use Cases
finally()
is useful in various scenarios:
- Resource Cleanup: Cleanup work that must be performed regardless of success or failure.
- Loading State Management: Hiding loading indicators.
- Database Connections: Closing database connections.
- Animation Control: Stopping loading animations.
let isLoading = true;
fetch('/api/data')
.then(response => response.json())
.then(data => processData(data))
.catch(error => showError(error))
.finally(() => {
isLoading = false;
document.getElementById('loading').style.display = 'none';
});
Implementation Principle and Compatibility
The implementation of finally()
can be thought of as a special then()
call:
Promise.prototype.finally = function(callback) {
return this.then(
value => Promise.resolve(callback()).then(() => value),
reason => Promise.resolve(callback()).then(() => { throw reason })
);
};
In terms of compatibility, modern browsers and Node.js support finally()
, but older environments may require a polyfill. Libraries like core-js or es6-promise can be used to provide support.
Common Pitfalls and Considerations
When using finally()
, keep the following points in mind:
- Errors in Callback Functions: If an error is thrown in the
finally()
callback, it will override the previous Promise state. - Return Values: The return value of
finally()
's callback is ignored unless it returns a rejected Promise. - Asynchronous Cleanup: If cleanup work needs to be done asynchronously, a Promise should be returned.
// Bad example: Errors in finally override the original result
Promise.resolve('Success')
.finally(() => { throw new Error('finally error') })
.catch(e => console.log(e.message)); // Output: finally error
// Correct handling of asynchronous cleanup
somePromise
.finally(() => {
return asyncCleanup(); // Return a Promise to ensure asynchronous cleanup completes
});
Integration with Other Asynchronous Features
finally()
can be effectively combined with other asynchronous features:
- async/await: Using
finally
in async functions. - Promise.all: Using it with combination methods like
Promise.all()
. - Cancelable Promises: Using
finally
for cleanup in cancellation logic.
async function fetchData() {
try {
const response = await fetch('/api/data');
return await response.json();
} catch (error) {
console.error('Failed to fetch data:', error);
throw error;
} finally {
console.log('Request completed');
}
}
// Combining with Promise.all
Promise.all([promise1, promise2])
.then(results => processResults(results))
.finally(() => console.log('All operations completed'));
Performance Considerations
While finally()
provides convenience, performance-sensitive scenarios require consideration:
- Microtask Queue:
finally()
increases the length of the microtask queue. - Memory Usage: Multiple
finally()
calls in long Promise chains can increase memory overhead. - Error Handling: Additional
finally()
callbacks may complicate error handling.
// Avoid unnecessary finally chains
// Not recommended
promise
.finally(() => {})
.finally(() => {})
.finally(() => {});
// Recommended
promise.finally(() => {
// Consolidate all cleanup logic
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:数据验证与输入过滤