嵌套 TryCatch 的奇怪行为

Odd behaviour of nested TryCatch

假设您有一个函数 f,它使用 TryCatch:

f <- function() {
  tryCatch(stop('Argh'), error = function(x) {
      message('f() error handler')
      warning(x)
    })
  message('computing')
  "Very Important Result"
}

>res <- f()
# f() error handler
# computing
# Warning message:
# In doTryCatch(return(expr), name, parentenv, handler) : Argh

>identical(res, "Very Important Result")
# [1] TRUE

到目前为止一切顺利。

但是现在:

tryCatch(x <- f(), error = function(x) {
  message('OUTER error handler !!!')
 })
# f() error handler
# OUTER error handler !!!

> x
Error: object 'x' not found

在这种情况下,计算从未达到表达式 f 中的消息('computing')。 问题:这是预期的吗?

try() 是通过 tryCatch() 实现的,因此不能仅凭这一点就无法按预期工作。

try
#> function (expr, silent = FALSE) 
#> {
#>     tryCatch(expr, error = function(e) {
#>         call <- conditionCall(e)
#>         if (!is.null(call)) {
#>             if (identical(call[[1L]], quote(doTryCatch))) 
#>                 call <- sys.call(-4L)
#>             dcall <- deparse(call)[1L]
#>             prefix <- paste("Error in", dcall, ": ")
#>             LONG <- 75L
#>             msg <- conditionMessage(e)
#>             sm <- strsplit(msg, "\n")[[1L]]
#>             w <- 14L + nchar(dcall, type = "w") + nchar(sm[1L], 
#>                 type = "w")
#>             if (is.na(w)) 
#>                 w <- 14L + nchar(dcall, type = "b") + nchar(sm[1L], 
#>                   type = "b")
#>             if (w > LONG) 
#>                 prefix <- paste0(prefix, "\n  ")
#>         }
#>         else prefix <- "Error : "
#>         msg <- paste0(prefix, conditionMessage(e), "\n")
#>         .Internal(seterrmessage(msg[1L]))
#>         if (!silent && identical(getOption("show.error.messages"), 
#>             TRUE)) {
#>             cat(msg, file = stderr())
#>             .Internal(printDeferredWarnings())
#>         }
#>         invisible(structure(msg, class = "try-error", condition = e))
#>     })
#> }
#> <bytecode: 0xa4a390>
#> <environment: namespace:base>

原来问题不在于嵌套 tryCatch(),而是 warning(x)。后者抛出错误(被 'outer' tryCatch() 捕获)。相应地改变你的例子给了我们预期的结果。

f <- function() {
  tryCatch(stop('Argh'), error = function(w) {
      message('f() error handler')
      warning(w$message)
    })
  message('computing')
  "Very Important Result"
}

tryCatch(x <- f(), error = function(e) str(e))
#> f() error handler
#> Warning in value[[3L]](cond): Argh
#> computing
x
#> [1] "Very Important Result"