跳至內容
Astera Technology

finally 可做收尾,但別不小心蓋過錯誤

收尾照做,原本的非同步錯誤也要交到 catch。

下載完整個案資料及參考答案 ↓

先追這段程式

本地 Promise 以 Local example failed 拒絕。async 函式在 finally 記錄 cleanup,呼叫者再用 catch 接住失敗。

完整原始程式
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})));
確實的 console 輸出
{"message":"Local example failed","trace":["cleanup"]}
值與執行位置
位置動作結果
await rejection進入 finallyLocal example failed
finally記錄一次 cleanup["cleanup"]
catch讀原錯誤訊息已處理拒絕

結果為何如此

finally 正常結束,保留之前的完成結果;若 return 或 throw,就會覆蓋。這裡是 async 內的 try/finally,並非直接使用 Promise.prototype.finally。例子每個拒絕都有處理。

比較另一種寫法

finally 內 return done 會蓋過失敗,令 async 呼叫變成成功。做了收尾不代表原工作成功。

另開的有限例子
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"]}

試清邊界

收尾本身再拋出 Cleanup failed,catch 會收到哪個錯誤?

查看參考答案

會收到 Cleanup failed,早前錯誤已被取代。如需保留兩項失敗,要明確處理收尾錯誤。

重新建立的邊界例子
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

原始例子檔案