golang defer 这个使用的执行流程一直很绕,所以决定写一篇文记录一下。

规则一:当defer被声明时,其参数就会被实时解析

案例一

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
    "fmt"
)

func main() {
    test()
}

func test() {
    defer f1(f2())

    fmt.Println("2")
    return
}

func f1(i int) int {
    return i
}

func f2() int {
    fmt.Println("1")
    return 1
}

输出:12

案例二

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import (
    "fmt"
)

func main() {
    defer getFunc()()
    fmt.Println("2")
}

func getFunc() func() {
    fmt.Println("1")
    return func() {
        fmt.Println("3")
    }
}

输出:123

注意区分传入的是函数变量还是函数

1、函数变量
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
    "fmt"
)

func main() {
    defer f3(f2)
    fmt.Println("2")
}

func f2() int {
  fmt.Println("1")
  return 1
}

func f3(f func() int) int {
  return f()
}

输出:21

2、函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
    "fmt"
)

func main() {
    defer f1(f2())
    fmt.Println("2")
}

func f1(i int) int {
  return i
}

func f2() int {
  fmt.Println("1")
  return 1
}

输出:12

规则二:defer执行顺序为先进后出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import (
    "fmt"
)

func main() {
    defer func() {
        fmt.Println("3")
    }()
    defer func() {
        fmt.Println("2")
    }()
    fmt.Println("1")
}

输出:123

规则三:defer可以读取有名返回值

1
2
3
4
5
6
7
8
func main() {
    i := 1
    defer func() {
        i++
        fmt.Println(i)
    }()
    fmt.Println(i)
}

输出:12

所有规则复合体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
    "fmt"
)

type msg struct {
    Num int
}

func (m *msg) print() {
    fmt.Println(m.Num)
}

func main() {
    m := new(msg) // 注意 new 返回一个指针
    m.Num = 1
    defer m.print()
    defer func(m msg) {
        m.print()
    }(*m)
    m.Num = 2
}

输出:12

可用于异常捕获

golang中 defer,panic,recover 是很常用的三个特性,三者一起使用可以充当其他语言中 try…catch… 的角色,而defer本身又像其他语言的析构函数。

可以在具体实现层 defer,panic,recover 也可以同时在最外层再 defer,panic,recover。异常捕获,按栈式,后入先出,一次性捕获,具体实现层捕获后,外层就不会再次捕获的。

panic其实就是c++中的throw exception

recover也是一个内建函数. recover就是c++中的catch.

  • recover如果想起作用的话, 必须在defered函数中使用.
  • 在正常函数执行过程中, 调用recover没有任何作用, 他会返回nil. 如这样:fmt.Println(recover()) // 输出 nil
  • 如果当前的goroutine panic了, 那么recover将会捕获这个panic的值, 并且让程序正常执行下去, 不会让程序crash.
1
2
3
4
5
6
defer func() {
    if r := recover(); r !=nil {
        logger.Sugar.Error(r)
        fmt.Println("Recovered in f", r)
    }
}

转载声明

作者:DukeAnn

链接:https://learnku.com/articles/43494#reply138836

来源:LearnKu

著作权归作者所有,任何形式的转载都请联系作者