說明
本文介紹一下使用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相關說明的參考:https://huoyingwhw.com/golangGuide/%E5%9F%BA%E7%A1%80/4/#82-defer