Trace the fixed program
The local promise rejects with Local example failed. The async function appends cleanup in finally and lets its caller catch the failure.
const trace=[];
async function run(){try{await Promise.reject(new Error('Local example failed'));}finally{trace.push('cleanup');}}
run().catch(error=>console.log(JSON.stringify({message:error.message,trace})));
{"message":"Local example failed","trace":["cleanup"]}| Step | Effect | Outcome |
|---|---|---|
| await rejection | Enters finally | Local example failed |
| finally | Append cleanup once | ["cleanup"] |
| catch | Read original error message | Rejected result handled |
Explain the result
A finally block that completes normally preserves the prior completion. A return or throw from finally overrides it. This example uses language try/finally inside an async function; Promise.prototype.finally has its own returned-promise semantics. Every rejecting promise here has a handler.
Test the alternative
return done inside finally suppresses the original failure and fulfills the async call. A cleanup label does not prove the operation succeeded.
const trace=[];async function run(){try{await Promise.reject(new Error('Local example failed'));}finally{trace.push('cleanup');return 'done';}}
run().then(value=>console.log(JSON.stringify({value,trace})),error=>console.log(error.message));
{"value":"done","trace":["cleanup"]}Work the boundary case
What error reaches catch if cleanup itself throws Cleanup failed?
Show the worked answer
Cleanup failed replaces the earlier Local example failed. Handle cleanup errors explicitly if both failures must be retained.
async function run(){try{await Promise.reject(new Error('Local example failed'));}finally{throw new Error('Cleanup failed');}}
run().catch(error=>console.log(error.message));
Cleanup failed