JS明明return成功卻拿到別的值:檢查finally裡的return

函式的try裡已經寫了return 'ready',呼叫端卻收到「done」?先往下看finally。在一般JavaScript函式中,try準備回傳後仍會執行finally;若finally又執行return,它會覆蓋原本的回傳結果。

兩個return,最後收到哪一個?

把下面整段貼到瀏覽器Console,或存成檔案用Node.js執行。這個例子沒有網路請求,只有函式的回傳順序。

function readStatus() {
  try {
    return 'ready'
  } finally {
    return 'done'
  }
}

console.log(readStatus()) // done

這不是try沒有執行,也不是呼叫端讀到舊值。程式準備把ready交回去時,先進入finally;那裡的第二個return改了最後結果。所以只在try的回傳行設中斷點,可能看不出問題。

不帶值的return也會覆蓋

有時候finally裡沒有寫替代值,只是加了一個看似無害的提早離開。它仍然會影響結果。

function readStatusWithoutValue() {
  try {
    return 'ready'
  } finally {
    return
  }
}

console.log(readStatusWithoutValue()) // undefined

呼叫端得到undefined,並不代表try沒有找到資料;也可能是finally中的空return把原值蓋掉了。不要用「finally沒有回傳新資料」判斷它不影響結果。

原本丟出的錯誤,也可能被藏起來

這個問題不只發生在成功分支。try已經丟出錯誤,但finally回傳一個普通值時,外層也可能收不到原本的錯誤。

function loadStatus() {
  try {
    throw new Error('讀取失敗')
  } finally {
    return 'fallback'
  }
}

try {
  console.log(loadStatus()) // fallback
} catch (error) {
  console.log('外層收到:', error.message)
}

這段會輸出fallback,不會進入外層catch。若你看到程式明明throw了,頁面卻像成功一樣繼續走,就要檢查finally是否有return。需要轉成預設結果時,應把錯誤處理規則寫在明確的catch或業務分支,別讓清理區順手吞掉例外。

把finally留給收尾,讓結果沿原路出去

finally適合恢復狀態或釋放已取得的資源。下面以記錄「收尾完成」代替實際資源操作;收尾區不回傳值,也不另外丟出錯誤。

function readStatusSafely() {
  try {
    return 'ready'
  } finally {
    console.log('收尾完成')
  }
}

console.log(readStatusSafely())
// 先印出:收尾完成
// 再印出:ready

拿掉finally的return後,原本的ready便能交回呼叫端。同樣地,若try丟出錯誤,而finally正常結束,原錯誤會繼續往外傳。實際收尾函式自己若拋錯,仍可能改變最後結果,因此也要安排它失敗時的處理方式。

先分清你用的是哪一種finally

這篇的例子都是try { ... } finally { ... }語法。promise.finally(callback)是另一個API,不能直接套用同一條規則:它的回呼正常回傳普通值時,通常不會用那個值替換原本Promise的結果;回呼拋錯或回傳被拒絕的Promise,才可能改變後續結果。

回到原程式時,先找try或catch的預期回傳值,再查finally裡有沒有return、throw或呼叫可能拋錯的收尾函式。用成功和失敗兩條路徑各跑一次,確認呼叫端仍收到原值或原錯誤。只確認「收尾有執行」,還不足以證明函式結果正確。

參考資料

原文鏈接:https://wntheme.com/js-finally-return-overrides-result/,轉載請註明出處。
0

評論0

顯示驗證碼
沒有帳號?註冊  忘記密碼?