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-04-09

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Nian糕的私人厨房

JavaScript 实现 Tab 点击切换

Tab 选项卡切换效果在现如今的网页中,运用的也是比较多的,包括点击切换、滑动切换、延迟切换、自动切换等多种效果,在这篇博文里,我们是通过原生 JavaScri...

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

CSS 变量教程

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

357110
来自专栏lgp20151222

jquery的html,text,val

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

10920
来自专栏我的博客

DOM基础知识

1.DOM介绍 DOM= Document Object Model,文档对象模型,DOM可以以一种独立于平台和语言的方式访问和修改一个文档的内容和结构。换句话...

30740
来自专栏c#开发者

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

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

34450
来自专栏搞前端的李蚊子

vue.js使用props在父子组件之间传参

本篇文章是我参考官方文档整理的,供大家参考,高手勿喷! prop 组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。要...

33440
来自专栏前端小叙

vue父组件向子组件动态传值的两种方法

在一些项目需求中需要父组件向子组件动态传值,比如我这里的需求是,父组件动态通过axios获取返回的图片url数组然后传给子组件,上传图片的子组件拿到该数组后进行...

790100
来自专栏编程微刊

Vue.js组件的重要选项

19320
来自专栏生信小驿站

python双Y轴

11720
来自专栏angularejs学习篇

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

13010

扫码关注云+社区

领取腾讯云代金券