Go-defer,panic,recover

defer

语法:

defer function_name() 

简单来讲,在defer所在函数执行完所有的代码之后,会自动执行defer的这个函数。

示例一(基本功能)

package main
import "fmt"

/*
D:\examples>go run helloworld.go
first
second

D:\examples>
*/
func main() {
    defer second()
    first()
}

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

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

示例二(函数局部性)

package main
import "fmt"

/*
D:\examples>go run helloworld.go
first
third
first
second

D:\examples>
*/
func main() {
    defer second()
    first()
    third()
}

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

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

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

示例三(栈特性)

package main
import "fmt"

/*
D:\examples>go run helloworld.go
first
third
second

D:\examples>
*/
func main() {
    defer second()
    defer third()
    first()
}

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

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

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

使用场景

主要用于资源需要释放的场景。比如打开一个文件,最后总是要关闭的。而在打开和关闭之间,会有诸多的处理,可能会有诸多的if-else、根据不同的情况需要提前返回。在传统语言中,return之前都需要一一调用close()。

而Go的defer就将事情变得简单了,open()之后,直接就用defer“注册”一个close()。伪代码:

f, = os.open(filename)
defer f.close()
do_something()
if (condition_a) {return}
do_something_again() 
if (condition_b) {return}
do_further_things()

panic & recover

先给出https://golang.org/pkg/builtin/上的函数说明。

panic

func panic(v interface{})

The panic built-in function stops normal execution of the current goroutine. When a function F calls panic, normal execution of F stops immediately. Any functions whose execution was deferred by F are run in the usual way, and then F returns to its caller. To the caller G, the invocation of F then behaves like a call to panic, terminating G’s execution and running any deferred functions. This continues until all functions in the executing goroutine have stopped, in reverse order. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. This termination sequence is called panicking and can be controlled by the built-in function recover.

recover

func recover() interface{}

The recover built-in function allows a program to manage behavior of a panicking goroutine. Executing a call to recover inside a deferred function (but not any function called by it) stops the panicking sequence by restoring normal execution and retrieves the error value passed to the call of panic. If recover is called outside the deferred function it will not stop a panicking sequence. In this case, or when the goroutine is not panicking, or if the argument supplied to panic was nil, recover returns nil. Thus the return value from recover reports whether the goroutine is panicking.

要点

  • panic相当于一个运行时异常
  • 遇到panic的时候,会停止当前函数剩下来的语句,但在退出该函数之前,会执行defer的语句
  • 依据函数调用层次,panic依次终止每个函数,直至main()。

panic示例

package main
import "fmt"

/*
D:\examples>go run helloworld.go
f.1
g.1
h.1
h.defer()
g.defer()
panic: panic in h()

goroutine 1 [running]:
panic(0x495360, 0xc04203a230)
        C:/Go/src/runtime/panic.go:500 +0x1af
main.h()
        D:/examples/helloworld.go:54 +0x12b
main.g()
        D:/examples/helloworld.go:45 +0xee
main.f()
        D:/examples/helloworld.go:38 +0xab
main.main()
        D:/examples/helloworld.go:29 +0x1b
exit status 2

D:\examples>
*/
func main() {
    f() // Line Number: 29
}

func final_print(msg string) {
    fmt.Println(msg)
}

func f() {
    fmt.Println("f.1")
    g() // Line Number: 38
    fmt.Println("f.2")
}

func g() {
    defer final_print("g.defer()")
    fmt.Println("g.1")
    h() // Line Number: 45
    fmt.Println("g.2")
}

func h() {
    defer final_print("h.defer()")
    fmt.Println("h.1")
    panic("panic in h()") // Line Number: 52
    fmt.Println("h.2")
}

panic & defer & recover

recover相当于try-catch的catch部分,使得panic不再传递。而defer相当于try-catch-final的final部分。

package main
import "fmt"

/*
D:\examples>go run helloworld.go
f.1
g.1
h.1
h.defer()
g.defer()
panic in h()
f.2

D:\examples>
*/
func main() {
    f()
}

func final_print(msg string) {
    fmt.Println(msg)
}

func f() {
    fmt.Println("f.1")
    g()
    fmt.Println("f.2")
}

func g() {
    defer func() {
          str := recover()
          fmt.Println(str)
    }()

    defer final_print("g.defer()")
    fmt.Println("g.1")
    h()
    fmt.Println("g.2")
}

func h() {
    defer final_print("h.defer()")
    fmt.Println("h.1")
    panic("panic in h()")
    fmt.Println("h.2")
}

获取数组元素

接下来再给一个例子,获取数组元素,处理数组访问越界的问题。

package main
import "fmt"

/*
D:\examples>go run helloworld.go
a[0]=1[true]
a[1]=2[true]
a[2]=3[true]
a[3]=4[true]
a[4]=5[true]
runtime error: index out of range [set to default value -1]
a[5]=-1[false]
runtime error: index out of range [set to default value -1]
a[6]=-1[false]
runtime error: index out of range [set to default value -1]
a[7]=-1[false]
runtime error: index out of range [set to default value -1]
a[8]=-1[false]
runtime error: index out of range [set to default value -1]
a[9]=-1[false]

D:\examples>
*/
func main() {
    a := [5]int {1,2,3,4,5}

    for i := 0; i < 10; i++ {
        item, ok := get(i, a)
        fmt.Printf("a[%d]=%d[%v]\n", i, item, ok)
    }
}

func get(i int, a [5]int) (ret int, ok bool) {
    ok = true 

    defer func() {
        err := recover()
        if err != nil {
            fmt.Println(err, "[set to default value -1]")
            ret = -1
            ok = false
        }
    }() 

    ret = a[i]
    return
}

原文发布于微信公众号 - Golang语言社区(Golangweb)

原文发表时间:2017-05-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏mySoul

js 和 css动画

使用setTimeout()或者setInterval()使用这两个函数定时调用一段代码。这是其原理。

2296
来自专栏生信小驿站

python双Y轴

1082
来自专栏菜鸟计划

vue内置指令详解——小白速会

指令 (Directives) 是带有 v- 前缀的特殊属性,职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。 内置指令 1、v-bin...

3965
来自专栏c#开发者

ios开发-Storyboard在多个viewcontroller之间导航的实现

IOS SDK6/Xcode4.5开始在Storyboad中新增很多功能对可视化的开发页面布局,导航更加方便,下面就写一下各种导航的实现。 1、不用像Xcode...

3345
来自专栏lgp20151222

jquery的html,text,val

    1.html()用为读取和修改元素的HTML标签     2.text()用来读取或修改元素的纯文本内容     3.val()用来读取或修改表单元素的...

1042
来自专栏Golang语言社区

Go-defer,panic,recover

defer 语法: defer function_name() 简单来讲,在defer所在函数执行完所有的代码之后,会自动执行defer的这个函数。 示例一(...

3008
来自专栏angularejs学习篇

关于文本框输入长度验证实现

1221
来自专栏coder修行路

CSS补充之--页面布局、js补充,dom补充

CSS补充之--页面布局 主站一:(下面是一个大致的模板) <div class="pg-header"> <div style="width...

3847
来自专栏前端菜鸟变老鸟

知识点:匹配字符串中的子串,并让子串红色显示、格式化输出json、元素点击之后hover失效、word-wrap:break-word和word-break:break-all

使用jQuery的click为某元素加上css样式,之后该元素原有的hover事件失效,原因是click加上的css权值比外联的css权值大。所以是点击之后原有...

952
来自专栏阮一峰的网络日志

CSS 变量教程

今年三月,微软宣布 Edge 浏览器将支持 CSS 变量。 这个重要的 CSS 新功能,所有主要浏览器已经都支持了。本文全面介绍如何使用它,你会发现原生 CSS...

35211

扫码关注云+社区

领取腾讯云代金券