go之panic和recover用法

657 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情

介绍

panic

用于中断执行,类似于java中的thow,抛出异常退出。

recover

用于捕获panic,使程序继续执行,类似于java中的catch,在异常的时候执行异常后应该执行的代码。和java不同的是recover只能是方法/函数的最后执行,不如java来的方便,但是利于程序员去抽出方法,相当于强制的规范。

思考

在go中其实panic用的比较少,直接返回error比较多,因为你要直接使用panic如果不用recover的话就会直接退出了,但是很多时候有些错误是需要拿到并且可以根据错误进行对应处理的,所以go的思想是错误不应该触发退出程序,你有的话需要返回给调用方提醒它去处理你的错误。

这样好处显而易见,相当于所有的错误都是java中的检查异常,但是如果你处理不了想直接往外抛,如果你使用返回error的话就要一路上带上它。

所以这时候就出现了panic,处理不了就往外抛,直到“顶层”你可以recover进行处理。

使用error的场景

需要让上层知道,让上层决定怎么处理,比如http的Do方法返回的就是error,需要你跟绝error决定怎么处理。

func (c *Client) Do(req *Request) (*Response, error) {
   return c.do(req)
}

如果是timeout相关的你就可以进行重试:

resp, err := httpClient.Do(req)

if err != nil {
   log.Error("doRequest error:", err)
   
   urlError, ok := err.(*url.Error)

   if ok && urlError.Timeout() {
      // todo retry
   }
}

使用panic的场景

还是上面的例子,你不可能无限重试,如果超过你限定的次数你就panic直接抛出退出,如果当前协程需要继续跑的话就recover住,如果不需要的话就直接退出:

resp, err := httpClient.Do(req)

if err != nil && i == (retryTimes-1) {
   panic(err)
}

panic和recover一起使用

其实上面也提到了,笔者有个比较讨巧的方式利用它来统一打印日志:

defer func() {
   if r := recover(); r != nil {
      log.Error("xxx Id:", id, "err:", r)
   }
}()

// todo first thing
if err != nil {
   panic(err)
}

// todo second thing
if err != nil {
   panic(err)
}

这样就不用每次判断err!=null去打印错误日志。