finally會執行嗎:try/catch的測試
翻譯練習
你知道
try
和catch
是怎麼工作的,但是你知道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/finally
在JavaScript
中的async/await
新語法中也能完美執行。所以這也提醒你去溫習一下async/await
,現在,是時候去逛逛我的Mastering Asynchronous JavaScript 課程了。