目錄
- Golang try catch
- panic
- panic主要使用場景:
- recover
- error
- 預定義錯誤值
- 自定義錯誤類型
- 使用 panic和recover模擬 tyr catch 謹慎!
Golang try catch
雖然在使用Golang的時候發(fā)現沒有try catch這種錯誤處理機制但是想一想golang作為一門優(yōu)雅的語言,似乎也是情理之中。因為在java中 throws在函數簽名中有一個關鍵字,旨在使異常流程比較簡潔,但是一旦預期的異常數量增加,就很難準確捕捉到具體異常所在。
雖然golang中并不提供try catch的寫法但是零值概念啊等等一些設計,在加上在panic的時候還可以使用recover進行處理,我覺得還是可以接受的。
在進入正題之前我們還是需要新了解一下panic和recover和error
panic
golang新手特別喜歡將panic當作exception(我也是這么過來的。。),而這么做會導致panic被濫用。
panic主要使用場景:
- 發(fā)生嚴重錯誤必須讓進行退出,嚴重的判斷標準是錯誤無法恢復導致程序無法執(zhí)行或繼續(xù)執(zhí)行或者繼續(xù)執(zhí)行也得不到預定的結果,另一些場景就是程序啟動需要的初始化數據需要在數據庫中讀取,這個時候數據庫無法讀取或者不存在配置項不可讀取,這個時候哪怕是執(zhí)行下去程序也是毫無意義的,這個時候panic暴露出問題反而是更可取的方式。非嚴重的錯誤比如客戶端不合法的請求參數返回錯誤參數信息提示即可,讓調用者自己去處理問題,而不是自己panic掛掉。
- 快速退出錯誤處理。也就是下面需要模擬的try catch的行為。大多數情況下錯誤處理都應該使用判斷error的機制,但是有時函數調用棧很深,逐層返回錯誤可能需要寫很多冗余代碼,這個時候可以使用panic讓程序的控制流直接跳到頂層的recover來進行處理。這種場景需要注意必須在包內就要recover。讓panic垮包傳遞可能會導致更復雜的問題,所以包的到處函數不應該產生panic。
recover
func recover() interface{}
recover 是一個內建的函數,用于重新獲得 panic 協程的控制。 只有在延遲函數的內部,調用 recover 才有用。在延遲函數內調用 recover,可以取到 panic 的錯誤信息,并且停止 panic 續(xù)發(fā)事件(Panicking Sequence),程序運行恢復正常。如果在延遲函數的外部調用 recover,就不能停止 panic 續(xù)發(fā)事件。
error
golang中內置的錯誤類型error是一個接口,自定義的錯誤類型必須實現error接口,這樣調用可以通過Error()獲取到具體的錯誤信息而不用關系錯誤的具體類型。
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
Error() string
}
很多語言限制函數只能有一個返回值,這就顯得尤為寶貴,而golang的多返回值語法糖避免了這種方式帶來的不便,錯誤值一般作為返回值列表的最后一個,其他返回值是成功執(zhí)行時需要返回的信息。也就衍生出了如下判斷:
if err != nil {
// error handling
} else {
// normal code
}
雖然這種錯誤處理方式代碼寫起來很蛋疼,但是golang風格確實推薦使用此種方式。
預定義錯誤值
var numIsZero = errors.New("num1 is zero")
var numIsNotZero = errors.New("num1 is not zero")
func GetInt(num1 int) (int, error) {
if num1 == 0 {
return num1, numIsZero
} else {
return num1, numIsNotZero
}
}
//比較錯誤
func ErrEquals() {
_, err := GetInt(1)
if err == numIsNotZero {
}
}
自定義錯誤類型
HTTP 表示客戶端的錯誤狀態(tài)碼有幾十個。如果為每種狀態(tài)碼都預定義相應的錯誤值,代碼會變得很繁瑣:
var ErrBadRequest = errors.New("status code 400: bad request")
var ErrUnauthorized = errors.New("status code 401: unauthorized")
這種場景下最佳的最法是自定義一種錯誤類型,并且至少實現 Error() 方法(滿足 error 定義):
type HTTPError struct {
Code int
Description string
}
func (h *HTTPError) Error() string {
return fmt.Sprintf("status code %d: %s", h.Code, h.Description)
}
這種方式下進行等值判斷時需要轉成具體的自定義類型然后取出 Code 字段判斷:
func request() error {
return HTTPError{404, "not found"}
}
func main() {
err := request()
if err != nil {
// an error occured
if err.(*HTTPError).Code == 404 {
// handle a "not found" error
} else {
// handle a different error
}
}
}
使用 panic和recover模擬 tyr catch 謹慎!
tyr catch 需要謹慎使用,因為panic / recover 和 try / catch 機制最大的不同在于控制流程上的區(qū)別。try / catch 機制控制流作用在 try 代碼塊內,代碼塊執(zhí)行到異常拋出點(throw)時,控制流跳出 try 代碼塊,轉到對應的 catch 代碼塊,然后繼續(xù)往下執(zhí)行。panic / recover 機制控制流則作用在整個 goroutine 的調用棧。當 goroutine 執(zhí)行到 panic 時,控制流開始在當前 goroutine 的調用棧內向上回溯(unwind)并執(zhí)行每個函數的 defer 。如果 defer 中遇到 recover 則回溯停止,如果執(zhí)行到 goroutine 最頂層的 defer 還沒有 recover ,運行時就輸出調用棧信息然后退出。所以如果要使用 recover 避免 panic 導致進程掛掉,recover 必須要放到 defer 里。為了避免過于復雜的代碼,最好不要使用嵌套的 defer ,并且 recover 應該直接放到 defer 函數里直接調用。
package main
import (
"fmt"
)
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println("error:", err)
}
}()
fmt.Println("start")
panic("Big Error")
fmt.Println("stop")
}
輸出:
start
error: Big Error
此部分的代碼相當于try部分的代碼一旦被panic 后面的代碼就不會被執(zhí)行了,而是跳到 defer部分
fmt.Println("start")
panic("Big Error")
fmt.Println("stop")
接收到錯誤并處理相當于catch:
defer func() {
if err := recover(); err != nil {
fmt.Println("error:", err)
}
}()
注意如果想再次catch需要按照從下往上的循序進行異常處理,原因的話了解defer。:
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println("error:", err)
}
}()
defer func() {
if err := recover(); err != nil {
fmt.Println("再次panic")
panic(err)
}
}()
fmt.Println("start")
panic("Big Error")
fmt.Println("stop")
}
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println("error:", err)
}
}()
defer func() {
if err := recover(); err != nil {
fmt.Println("再次panic")
panic(err)
}
}()
defer func() {
if err := recover(); err != nil {
fmt.Println("再次panic")
panic(err)
}
}()
defer func() {
if err := recover(); err != nil {
fmt.Println("再次panic")
panic(err)
}
}()
fmt.Println("start")
panic("Big Error")
fmt.Println("stop")
}
輸出:
start
再次panic
再次panic
再次panic
error: Big Error
到此這篇關于Golang try catch與錯誤處理的實現的文章就介紹到這了,更多相關Golang try catch與錯誤處理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- Golang中重復錯誤處理的優(yōu)化方法
- Golang巧用defer進行錯誤處理的方法
- golang 語言中錯誤處理機制