错误+延迟执行+panic&recover

1 error 接口

Go 语言通过 error 接口实现错误处理的标准模式,通过使用函数返回值列表中的最后一个值返回错误信息,将错误的处理交由程序员主动进行处理

在程序中运行时错误错误一般分为两种:

  • 可恢复的错误(重试/或忽略)

  • 不可恢复的错误(程序退出)

范例代码:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    // 需要给调用者返回错误信息
    // 通过函数最后一个返回值返回错误!
    // 调用者需要对错误进行检查,决定如何操作
    
    v, err := strconv.Atoi("123")
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(v)
    }
}

1.2 error 接口初始化方法

下面两种效果其实是一样的,并没有区别

1.2.1 第一种初始化 error 方式

通过 fmt.Errorf 方法创建

定义函数实现 error 范例:

package main

import (
    "fmt"
)

// error 接口类型
func div(left, right int) (int, error) {
    if right == 0 {
        // 在 go 中通过, fmt.Errorf 就看返回错误
        return 0, fmt.Errorf("right num is zeror")
    }

    return left / right, nil
}

func main() {
    res, err := div(10, 0)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(res)
    }
}

1.2.2 第二种初始化 error 方法

通过 errors 包的 New 方法创建

范例代码:

package main

import (
    "errors"
    "fmt"
)

// error 接口类型
func div(left, right int) (int, error) {
    if right == 0 {
        // 在 go 中通过, errors.New 即可返回错误
        return 0, errors.New("传递参数错误")
    }

    return left / right, nil
}

func main() {
    res, err := div(10, 0)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(res)
    }
}

1.3 对 error 错误进行处理

看下面代码,如果说传递个 div 的参数是错误的话,我们可以进行调试

package main

import (
    "errors"
    "fmt"
)

// error 接口类型
func div(left, right int) (int, error) {
    if right == 0 {
        return 0, errors.New("right num is 0")
    }

    return left / right, nil
}

func main() {
    if res, err := div(10, 0); err != nil {
        fmt.Println("value is error :", err)
    } else {
        fmt.Println(res)
    }
}

2 defer 延迟执行-压栈

defer 关键字用户声明函数,不论函数是否发生错误都在函数执行最后执行(return 之前),若使用 defer 声明多个函数,则按照声明的顺序,先声明后执行(堆)常用来做资源释放,记录日志等工作

我们可以看到通过下面代码,defer 的执行顺序是最后执行,也就是说当函数存在其他代码块的时候,优先执行其他代码块,最后在执行 defer ,而且当有多个 defer 的时候是先进后出

package main

import "fmt"

func main() {
    fmt.Println("start")

    defer func() {
        fmt.Println("defer")
    }()

    defer func() {
        fmt.Println("12")
    }()

    fmt.Println("END")
}

通过输出,我们会还想 fmt.Println("defer") 是先进,但是后出

所以 defer 的执行顺序是先进后出,谁先声明谁后执行

2.1 defer 细节1

我们可以通过下面代码发现, defer 并不受循环影响。

package main

import "fmt"

func main() {
    for i := 0; i < 2; i++ {
        fmt.Println("start")
        defer func() {
            
            // 这里的  i 寻找的是 for 循环的 i 
            fmt.Printf("defer %d\n", i)
        }()
        fmt.Println("stop")
    }
}

输出结果看到,defer 2 ,为什么会是 2 呢,因为 defer 在使用的时候其实使用了外部的变量,所以最后 i 都成 2 了,由于 for 循环完毕执行了 i++

可以通过下面代码解决上面问题

每次执行完了之后我们将 i 传递到 defer func() 中去

package main

import "fmt"

func main() {
    for i := 0; i < 2; i++ {
        fmt.Println("start")
        defer func(i int) {
            fmt.Printf("defer %d\n", i)
        }(i)
        fmt.Println("stop")
    }
}

延迟执行或者闭包的时候,参数传递的时候一定要注意

2.2 defer 细节2

在延迟执行中尽量不要修改返回值

package main

import "fmt"

func test() (i int) {
    // 在延迟执行中尽量不要修改返回值
    
    i = 1
    defer func() {
        fmt.Println("defer")
        
        // 在 defer 中将 i 修改为 2
        i = 2
    }()
    return i
}

func main() {
    fmt.Println(test())
}

3 panic & recover

go 语言提供 panic 和 recover 函数用于处理运行时错误,当调用 panic 抛出错误,中断原有的控制流程,常用于不可修复性错误。recover 函数用于终止错误处理流程,仅在 defer语句的函数中有效,用于截取错误处理流程,recover 只能捕获到最后一个错误

处识 panic

panic 其实是在程序员执行程序的时候用来抛出错误

package main

func main() {
    panic("我是错误")
}

当出现错误,一般要用延迟执行加 recover

package main

import "fmt"

func main() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("出错了,进行恢复", err)
        }
    }()
    panic("我是错误")
}

假如我们把 panic 注释掉就不抛出错误,因为 recover 只能够捕获 panic

package main

import "fmt"

func main() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("出错了,进行恢复", err)
        }
    }()
//  panic("我是错误")
}

通过函数调用实现控制 recover 和 panic

package main

import "fmt"

func test(p bool) {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("出现错误:", err)
        }
    }()
    if p {
        panic("panic error")
    }
    fmt.Println("running...")
}

func main() {
    test(true)
    test(false)
}

recover 还能够捕获其他函数中的 painc

package main

import "fmt"

func callback(p bool) {
    if p {
        panic("callback panic")
    }
    fmt.Println("running...")
}

func test(p bool) {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("出错:", err)
        }
    }()
    // 传入 callback 函数调用 p
    callback(p)
}

func main() {
    test(true)
    test(false)
}

通过输出发现 recover 也能捕获到其他函数的错误

3.1 recover 的处理

recover 的处理,一般会用 recover 捕获到错误,然后进行返回

package main

import "fmt"

func callback(p bool) {
    if p {
        panic("callback panic")  // p 为 true 返回 panic
    }
    fmt.Println("running...")
}

// 定义返回值为 error 类型
func test(p bool) (err error) {
    defer func() {
        if msg := recover(); msg != nil { // 定义 msg 接收 recover 的返回值,捕获panic
            err = fmt.Errorf("%s", msg)
        }
    }()
    callback(p)
    
    // 返回命名返回值
    return
}

func main() {
    fmt.Println(test(true))
    fmt.Println(test(false))
    fmt.Println(test(false))

}

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇