1. 程式人生 > 實用技巧 >finally會執行嗎:try/catch的測試

finally會執行嗎:try/catch的測試

翻譯練習

原部落格地址:Will it finally: a try/catch quiz

你知道trycatch是怎麼工作的,但是你知道finally是怎麼工作的嗎?它是在丟擲異常後執行還是在return語句後執行?

隨著async/await的出現,我發現我最近在我的程式碼裡try/catch/finally用的越來越多。但是老實說,我對finally有一些陌生。當我實際去使用它的時候,我對它的具體細節有一點不確定。所以我舉例幾個例子。

當你丟擲一個catch

考慮在catch中丟擲一個異常這種情況。在你退出函式之前沒有東西去捕獲丟擲的異常。這個時候,finally會執行嗎?為了找到答案,在編輯器的底部取消example()

呼叫的註釋!

function example() {
  try {
    fail()
  }
  catch (e) {
    console.log("Will finally run?")
    throw e
  }
  finally {
    console.log("FINALLY RUNS!")
  }
  console.log("This shouldn't be called eh?")
}

// example()

Result:

Will finally run?
FINALLY RUNS!
errorReferenceError: fail is not defined
    at fail (index.js:3:4)
    at example (index.js:15:0)
    at e.execute (https://frontarm.com/8058525ee5952d818c1c0e294c9d4365.js:1:29826)
    at handleUnrequiredPrepared (https://frontarm.com/8058525ee5952d818c1c0e294c9d4365.js:1:26656)

finally運行了,儘管最後一個log語句沒有執行!你可以看到finally有一點特殊;它允許你在丟擲一個錯誤和離開函式之前執行,儘管這個錯誤是在一個catch塊內丟擲的。

沒有catch試試

你知道如果你提供了一個finally塊,你可以不提供catch塊嗎?你可能知道,但這值得一問!

所以下一個問題:即使在try塊中沒有發生錯誤,finally塊會執行嗎?如果你不確定,取消編輯器的最後一行程式碼來找出答案。

function example() {
  try {
    console.log("Hakuna matata")
  }
  finally {
    console.log("What a wonderful phrase!")
  }
}

// example()
Hakuna matata
What a wonderful phrase!

是的,儘管沒有任何錯誤,finally還是執行了。當然,在錯誤發生的時候它也會執行。這就是finally的目的--它允許你處理這兩種情況,就像你在下面的例子中看到的:

function example() {
  try {
    console.log("I shall try and fail");
    fail();
  }
  catch (e) {
    console.log("Then be caught");
  }
  finally {
    console.log("And finally something?");
  }
}

// example()
I shall try and fail
Then be caught
And finally something?

Return 和 finally

所以,finally允許你在錯誤發生後進行一些清理。但是,如果沒有任何錯誤發生,僅僅是函式的在try塊中的一個正常的return語句會怎樣呢?

看看下面的例子。當你已經執行過return語句,example() 函式中的finally塊還會執行嗎?在編輯器底部取消example()的註釋然後找出答案。

function example() {
  try {
    console.log("I'm picking up my ball and going home.")
    return
  }
  finally {
    console.log('Finally?')
  }
}

// example()
I'm picking up my ball and going home.
Finally?

規則

try/catch/finally中,finally塊總會執行--哪怕提前執行到return語句或者丟擲異常。
這就是它為什麼這麼有用。這是編寫那些不管怎樣都要執行的程式碼的完美場所,像是處理異常I/O的程式碼。事實上,這就是這篇文章的靈感來源。

我用finally幹什麼

Frontend Armory是一個靜態渲染的網站,它是通過一個叫Navi的工具構建的,它允許你配置一個renderPageToString()函式,這個函式用來渲染每個頁面。

為了確認renderPageToString()函式的每次呼叫都獨立於上一次的呼叫,Navi用try/catch/finally 和一些obscure node-fu來解除安裝那些在渲染過程中載入的模組。

let oldCacheKeys = new Set(Object.keys(require.cache))
try {
  html = await renderPageToString(options)
}
catch (e) {
  throw e
}
finally {
  process.env.NODE_ENV = nodeEnv

  for (let key of Object.keys(require.cache)) {
    if (!oldCacheKeys.has(key)) {
      delete require.cache[key]
    }
  }
}

從上面的例子可以看出,try/catch/finallyJavaScript中的async/await新語法中也能完美執行。所以這也提醒你去溫習一下async/await,現在,是時候去逛逛我的Mastering Asynchronous JavaScript 課程了。