error 是一种类型,表示错误状态的类型,如果没有错误则是nil。直白点将:error 类型就是描述错误的一种类型。
panic 在golang goroutine 的作用
panic 官方文档介绍:
panic 是用来停止当前程序的执行。当一个方法调用panic。 当函数F调用panic时,F的正常执行立即停止。 但是任何有F推迟的函数都会运行,意思是F定义有defer关键字声明的函数会执行,然后F返回给它的调用者。 对于调用者G来说,F的调用就像调用panic 一样,终止G的执行并运行任何延迟(带有defer 关键字)的函数。 这种情况会持续下去,直到正在执行的goroutine中的所有功能都以相反的顺序停止。 此时,程序终止并报告错误情况,包括panic的参数值。最后这种情况可以通过调用recover 来恢复函数的运行。
函数 recover 介绍
recover内置函数允许一段程序管理一个正在paincing goroutine的行为。
在defer 定义的函数(不是由它调用的任何函数)内部执行一段recover 函数,通过recover函数执行来停止panic 函数的执行,并且可以找出给panic所传递的错误值。 如果在defer 函数之外调用恢复,它不会停止panic的执行。 在这种情况下,或者当goroutine没有panicing时,或者提供给panicing的参数为零时,恢复返回nil。 因此,recover函数的返回值报告协程是否正在遭遇panicing 。
panic函数就是往外扔错误,一层接一层往上扔直到当前程序不能运行为止,不想让panic 函数扔的错误导致程序挂掉,就得使用recover 函数来接收panic 错误或者说是阻挡panicing ,并且recover 函数可以将错误转化为error 类型。因为panic 错误不会让defer 关键字定义的函数也停止运行,就是说defer 关键字声明的函数或者代码即使遇到错误也会执行。
一个函数里面有defer 关键字声明一个函数(假设叫catch 函数)和要运行出错的代码,在catch 函数里面调用recover 函数。recover 会拦截错误,不会让错误往上扔,返回给调用者error(里面有错误的信息)类型 ,从而使goroutine 不挂掉。
上代码:
package main
import (
"fmt"
"errors"
)
func main() {
testError()
afterErrorfunc()
}
func testError() {
//defer catch()
panic(" \"panic 错误\"")
fmt.Println("抛出一个错误后继续执行代码")
}
func catch() {
if r := recover(); r != nil {
fmt.Println("testError() 遇到错误:", r)
var err error
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("")
}
if err != nil {
fmt.Println("recover后的错误:",err)
}
}
}
func afterErrorfunc(){
fmt.Println("遇到错误之后 func ")
}
运行结果:
panic: "panic 错误"
goroutine 1 [running]:
main.testError()
E:/goCode/src/MyTestGo/src/com.dylan.main/panic/testpanic.go:16 +0x40
main.main()
E:/goCode/src/MyTestGo/src/com.dylan.main/panic/testpanic.go:10 +0x27
Process finished with exit code 2
当panic 函数执行的时候导致后面函数 afterErrorfunc() 不能执行,main函数也抛出一个错误,整个程序异常退出。
通过defer 关键字调用 catch函数。
func testError() {
defer catch()
panic(" \"panic 错误\"")
fmt.Println("抛出一个错误后继续执行代码")
}
程序运行结果:
testError() 遇到错误: "panic 错误"
recover后的错误: "panic 错误"
遇到错误之后 func
Process finished with exit code 0
分析:程序正常结束,没有因为panic(错误)而到导致程序终止挂掉。错误被recover 函数接收,转化为error类型的错误,最后输出“ recover后的错误: “panic 错误” ” 而且后面 afterErrorfunc()执行。
一般在写的时候这么写 不用定义catch 函数:
func main() {
testError()
afterErrorfunc()
}
func testError() {
defer func() {
if r := recover(); r != nil {
fmt.Println("testError() 遇到错误:", r)
}
}()
panic(" \"panic 错误\"")
fmt.Println("抛出一个错误后继续执行代码")
}
func catch(err error) {
if r := recover(); r != nil {
fmt.Println("testError() 遇到错误:", r)
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("")
}
}
}
func afterErrorfunc() {
fmt.Println("遇到错误之后 func ")
}
在发生panic 函数里面加入下述代码就可以拦截panicing, 并且不让程序挂掉和显示错误信息。
defer func() {
if r := recover(); r != nil {
fmt.Println("testError() 遇到错误:", r)
}
}()
最后如果想将错误信息返回给调用者可以这么做:
func main() {
err := testError()
if err != nil {
fmt.Println("main 函数得到错误类型:", err)
}
afterErrorfunc()
}
func testError() (err error) {
defer func() {
if r := recover(); r != nil {
fmt.Println("testError() 遇到错误:", r)
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("")
}
}
}()
panic(" \"panic 错误\"")
fmt.Println("抛出一个错误后继续执行代码")
return nil
}
func catch(err error) {
if r := recover(); r != nil {
fmt.Println("testError() 遇到错误:", r)
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("")
}
}
}
func afterErrorfunc() {
fmt.Println("遇到错误之后 func ")
}
提前声明一个error 类型的变量。把错误信息传递给error变量,再把error变量返回给调用者。