美文网首页
defer ,panic,recover

defer ,panic,recover

作者: 普朗tong | 来源:发表于2020-04-25 23:27 被阅读0次

    defer的执行顺序

    多个defer出现的时候,它是一个“栈”的关系,也就是先进后出。一个函数中,写在前面的defer会比写在后面的defer调用的晚。

    package main
    
    import "fmt"
    
    func main() {
        defer func1()
        defer func2()
        defer func3()
    }
    
    func func1() {
        fmt.Println("A")
    }
    
    func func2() {
        fmt.Println("B")
    }
    
    func func3() {
        fmt.Println("C")
    }
    //输出结果:C B A
    

    defer和return的先后顺序

    return之后的语句先执行,defer后的语句后执行。

    package main
    
    import "fmt"
    
    func deferFunc() int {
        fmt.Println("defer func called")
        return 0
    }
    
    func returnFunc() int {
        fmt.Println("return func called")
        return 0
    }
    
    func returnAndDefer() int {
    
        defer deferFunc()
    
        return returnFunc()
    }
    
    func main() {
        returnAndDefer()
    }
    //输出
    //return func called
    //defer func called
    

    defer 无名返回值 有名返回值

    • 函数返回值初始化
      该知识点不属于defer本身,但是调用的场景却与defer有联系,所以也算是defer必备了解的知识点之一。
      如 : func DeferFunc1(i int) (t int) {}
      其中返回值t int,这个t会在函数起始处被初始化为对应类型的零值并且作用域为整个函数。
    package main
    
    import "fmt"
    
    func DeferFunc(i int) (t int) {
    
        fmt.Println("t = ", t)
    
        return 2
    }
    
    func main() {
        DeferFunc(10)
    }
    //输出
    //t =  0
    
    • defer、return 和无名的返回值
    package main
    
    func main() {
        name := run()
        println("return: name = " + name) 
    }
    
    func run() (string) {
        name := "Paul"
        defer sayHello(&name)
        name = "John"
        return name
    }
    
    func sayHello(name *string) {
        *name = "George"
        println("Hello " + *name)
    }
    // 输出
    // Hello George
    // return: name = John
    
    • defer、return 和有名返回值
    //传指针
    package main
    
    func main() {
        name := run()
        println("return: name = " + name)
    }
    
    func run() (x string) {
        name := "Paul"
        x = name
        defer sayHello(&x)
        name = "John"
        return name
    }
    
    func sayHello(name *string) {
        println("Hello " + *name)
        *name = "George"
        println("Hello " + *name)
    }
    //输出
    //Hello John
    //Hello George
    //return: name = George
    
    //传值
    package main
    
    func main() {
        name := run()
        println("return: name = " + name)
    }
    
    func run() (x string) {
        name := "Paul"
        x = name
        defer sayHello(x)
        name = "John"
        return name
    }
    
    func sayHello(name string) {
        println("Hello " + name)
        name = "George"
        println("Hello " + name)
    }
    
    //输出
    //Hello Paul  
    //Hello George 
    //return: name = John
    

    说明:defer,return 和返回值三者之间的执行逻辑
    return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出

    defer 闭包

    • 闭包是什么
      闭包是由函数及其相关引用环境组合而成的实体,即
      闭包=函数+引用环境

    一般的函数都有函数名,但是匿名函数就没有。匿名函数不能独立存在,但可以直接调用或者赋值于某个变量。匿名函数也被称为闭包,一个闭包继承了函数声明时的作用域。在Golang中,所有的匿名函数都是闭包。

    有个不太恰当的例子,可以把闭包看成是一个类,一个闭包函数调用就是实例化一个类。闭包在运行时可以有多个实例,它会将同一个作用域里的变量和常量捕获下来,无论闭包在什么地方被调用(实例化)时,都可以使用这些变量和常量。而且,闭包捕获的变量和常量是引用传递,不是值传递。

    package main
    
    func main() {
        name := run()
        println("return: name = " + name)
    }
    
    func run() (string) {
        name := "Paul"
        aFun := func() {
            println("Hello " + name)
            name = "George"
            println("Hello " + name)
        }
        name = "John"
        aFun()
        bfunc:= func() {
            println("Hello " + name)
            name = "Bobbi"
            println("Hello " + name)
        }
        bfunc()
        return name
    }
    //输出
    //Hello John
    //Hello George
    //Hello George
    //Hello Bobbi
    //return: name = Bobbi
    
    • defer与闭包
    package main
    
    func main() {
        name := run()
        println("return: name = " + name)
    }
    
    func run() (x string) {
        name := "Paul"
        aFun := func() {
            println("Hello " + x)
            x = "George"
            println("Hello " + x)
        }
        defer aFun()
        name = "John"
        return name
    }
    //输出
    //Hello John
    //Hello George
    //return: name = George
    
    
    
    
    package main
    
    func main() {
        name := run()
        println("return: name = " + name)
    }
    
    func run() (x string) {
        name := "Paul"
        x = name
        aFun := func(x string) {
            println("Hello " + x)
            x = "George"
            println("Hello " + x)
        }
        defer aFun(x)
        name = "John"
        return name
    }
    //输出:
    //Hello Paul
    //Hello George
    //return: name = John
    

    defer panic recover

    能够触发defer的是遇见return(或函数体到末尾)和遇见panic。
    遇见panic的情况:遍历本协程的defer链表,并执行defer。在执行defer过程中:遇到recover则停止panic,返回recover处继续往下执行。如果没有遇到recover,遍历完本协程的defer链表后,向stderr抛出panic信息。
    A. defer遇见panic,但是并不捕获异常的情况

    package main
    
    import (
        "os"
        "fmt"
        "time"
    )
    
    func main() {
        var user = os.Getenv("USER_")
        go func() {
            defer func() {
                fmt.Println("defer here")
            }()
    
            if user == "" {
                panic("should set user env.")
            }
        }()
    
        time.Sleep(1 * time.Second)
        fmt.Printf("get result %d\r\n", 1)
    }
    //输出
    //defer here
    //panic: should set user env.    
    

    说明:会发现defer中的字符串”defer here”打印出来了,而main流程中的”ger result”没有打印,说明panic坚守了自己的原则:
    执行,且只执行,当前goroutine的defer

    B.defer遇见panic,并捕获异常

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        defer_call()
    
        fmt.Println("main 正常结束")
    }
    
    func defer_call() {
    
        defer func() {
            fmt.Println("defer: panic 之前1, 捕获异常")
            if err := recover(); err != nil {
                fmt.Println(err)
            }
        }()
    
        defer func() { fmt.Println("defer: panic 之前2, 不捕获") }()
    
        panic("异常内容") //触发defer出栈
    
        defer func() { fmt.Println("defer: panic 之后, 永远执行不到") }()
    }
    
    //输出
    //defer: panic 之前2, 不捕获
    //defer: panic 之前1, 捕获异常
    //异常内容
    //main 正常结束
    

    参考:
    Golang中的Defer必掌握的7知识点
    Golang中defer、闭包以及命名返回值
    Golang: 深入理解panic and recover

    相关文章

      网友评论

          本文标题:defer ,panic,recover

          本文链接:https://www.haomeiwen.com/subject/vculwhtx.html