说明

  本文介绍一下使用recover捕获panic的操作及遇到的一个坑。

使用recover捕获panic

  正常情况下,发生panic的函数会导致程序异常退出,我们可以使用defer语句在另外一个函数中捕获到当前函数panic的error并做相应的逻辑处理:

package test1

import (
    "fmt"
    "testing"
)

func TestRecover(t *testing.T) {

    handleSth()

}

// 返回的函数
func sendResponse(err string) {
    // 有错误返回错误信息
    if err != "" {
        fmt.Println("发生异常:", err)
    } else {
        println("成功sendResponse")
    }
}

// 专门用于捕获异常的
func rec1() {
    err := recover()
    // 注意这里捕获到的err是一个interface,需要与nil做比较
    if err != nil {
        sendResponse(err.(string))
    } else {
        sendResponse("")
    }
}

// 业务代码,可能发生异常
func handleSth() {

    // 如果函数中发生了异常会被rec1()函数捕获到
    defer rec1()
    // 模拟业务代码中发生panic的情况
    panic("发生了异常...")

}

遇到的问题

正确的方式

  正常情况下,我们使用下面的方式去捕获panic:

package test1

import (
    "fmt"
    "testing"
)

func rec1() {
    err :=  recover()
    if err != nil{
        fmt.Println("err: ", err)
    }
}

func TestRecover(t *testing.T) {

    // 正确的捕获方式
    defer rec1()

    panic("TestRecover raises error!")
}

错误的方式

  下面这种方式捕获不到panic:

package test1

import (
    "fmt"
    "testing"
)

func rec1() {
    err :=  recover()
    if err != nil{
        fmt.Println("err: ", err)
    }
}



func TestRecover(t *testing.T) {

    // 错误的捕获方式
    defer func(){
        // 这里加一些其他的业务逻辑
rec1() }() panic("TestRecover raises error!") }

  上面这种方法其实是想在recover到panic之前再做一些业务处理,结果没有捕获到想要捕获的panic会导致程序崩溃。

  看了一下相关资料,解释应该是每一个recover应该“直接”与对应的panic配对,也就是说recover应该只能捕获到与其同一个作用域里的panic(具体原理还需要深入学习)。

  像上面的那种写法应该在匿名函数中写panic才可以:

func TestRecover(t *testing.T) {
    // 错误的捕获方式
    func(){
        // 这里写一些其他的业务逻辑
        defer rec1()
        panic("TestRecover raises error!")
    }()
}

  关于Golang中defer、panic与recover相关说明的参考: