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 课程了。