項目中,有時爲了讓程序更健壯,也即不panic
,咱們或許會使用recover()
來接收異常並處理。git
好比如下代碼:github
func NoPanic() { if err := recover(); err != nil { fmt.Println("Recover success...") } } func Dived(n int) { defer NoPanic() fmt.Println(1/n) }
func NoPanic()
會自動接收異常,並打印相關日誌,算是一個通用的異常處理函數。golang
業務處理函數中只要使用了defer NoPanic()
,那麼就不會再有panic
發生。數據庫
關因而否應該使用recover接收異常,以及什麼場景下使用等問題不在本節討論範圍內。 本節關注的是這種用法的一個變體,曾經出如今筆者經歷的一個真實項目,在該變體下,recover再也沒法接收異常。函數
在項目中,有衆多的數據庫更新操做,正常的更新操做須要提交,而失敗的就須要回滾,若是異常分支比較多, 就會有不少重複的回滾代碼,因此有人嘗試了一個作法:即在defer中判斷是否出現異常,有異常則回滾,不然提交。日誌
簡化代碼以下所示:code
func IsPanic() bool { if err := recover(); err != nil { fmt.Println("Recover success...") return true } return false } func UpdateTable() { // defer中決定提交仍是回滾 defer func() { if IsPanic() { // Rollback transaction } else { // Commit transaction } }() // Database update operation... }
func IsPanic() bool
用來接收異常,返回值用來講明是否發生了異常。func UpdateTable()
函數中,使用defer來判斷最終應該提交仍是回滾。協程
上面代碼初步看起來還算合理,可是此處的IsPanic()
不再會返回true
,不是IsPanic()
函數的問題,而是其調用的位置不對。get
上面代碼IsPanic()
失效了,其緣由是違反了recover的一個限制,致使recover()失效(永遠返回nil
)。it
如下三個條件會讓recover()返回nil
:
nil
;(通常panic語句如panic("xxx failed...")
)前兩條都比較容易理解,上述例子正是匹配第3個條件。
本例中,recover() 調用棧爲「defer (匿名)函數」 --> IsPanic() --> recover()。也就是說,recover並無被defer方法直接調用。符合第3個條件,因此recover() 永遠返回nil。
贈人玫瑰手留餘香,若是以爲不錯請給個贊~
本篇文章已歸檔到GitHub項目,求星~ 點我即達