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

posted @ 2020-12-15 20:49  rain_watcher  阅读(285)  评论(0编辑  收藏  举报