当前位置: 首页 > news >正文

爱上开源之golang入门至实战第四章函数(Func)(十)

爱上开源之golang入门至实战第四章函数(Func)(十)

前言

函数是各种编程语言里组成编程逻辑的主要部分;特别在Go语言中,Go语言削弱了面向对象编程的一些特性,比如构造函数,多态,抽象等,在函数式编程方面进行了类似于javascript和python等语言的借鉴,由此在咱们的第四章函数章节里,已经就细化了十个章节来进行各个方面的介绍。今天的这个文章,接着一文中对defer和recover的介绍,进一步深入的通过代码样例来学习函数。

 

Recover的顺序

我们把上一文中对processErrorPlus的调用代码,进行一下改造,把recover的processErrorPlus的defer调用,放在第二个defer之后,会有什么样的效果呢。

调整后的代码

func TestDeferFun8(t *testing.T) {
   defer fmt.Println("Defer Func 1")
​
   defer processErrorPlus(func(err error) {
      fmt.Printf("Defer Func 2 error: %v\n", err)
   })
​
   defer fmt.Println("Defer Func 3")
​
   fmt.Println("Test")
​
   panic("This is a testing")
}
​
​
===== OUTPUT =====
Test
Defer Func 3
Defer Func 2 error: This is a testing
Defer Func 1

可以看到Recover在Defer Func 2中已经成功调用, 对后面的defer次序没有影响。

Recover重复处理Error

现在在每个defer都进行processErrorPlus调用, 并且修改processErrorPlus函数,函数processErrorPlus分别对recover存在error进行处理,和没有进行处理,修改后的processErrorPlus;如下代码

type DoError func(err error)
type DoNotError func()
​
func processErrorPlus2(doError DoError, notError DoNotError) {
    if e := recover(); e != nil {
        if doError != nil {
            var err error
            switch x := e.(type) {
            case string:
                err = errors.New(x)
            case error:
                err = x
            default:
                err = errors.New("unknown panic")
            }
​
            doError(err)
        }
    } else {
        if notError != nil {
            notError()
        }
    }
}

如上代码,processErrorPlus2传入两个参数,都是函数类型的参数, 一个用来进行recover有error时的回调函数,一个用来进行recover没有error时的回调函数; 把主要的函数也进行修改, 三个defer函数都是用processErrorPlus2的函数处理;代码如下:

​
func TestDeferFun9(t *testing.T) {
    defer processErrorPlus2(func(err error) {
        fmt.Printf("Defer Func 1 error: %v\n", err)
    }, func() {
        fmt.Printf("Defer Func 1 \n")
    })
​
    defer processErrorPlus2(func(err error) {
        fmt.Printf("Defer Func 2 error: %v\n", err)
    }, func() {
        fmt.Printf("Defer Func 2 \n")
    })
​
    defer processErrorPlus2(func(err error) {
        fmt.Printf("Defer Func 3 error: %v\n", err)
    }, func() {
        fmt.Printf("Defer Func 3 \n")
    })
​
    fmt.Println("Test")
​
    panic("This is a testing")
}
​
​
===== OUTPUT =====
Test
Defer Func 3 error: This is a testing
Defer Func 2 
Defer Func 1 

通过上面的代码样例,从执行结果来看, 当主函数里使用panic声明出异常;在依次执行三个defer函数中,

先执行Defer Func 3这个processErrorPlus2的defer函数调用,在processErrorPlus2函数中,通过recover获取当前的error,此时error存在,故执行了传入的DoError的回调函数;也就输出了“Defer Func 3 error: This is a testing”;

Func3段执行结束后,接下来执行Func2这段的processErrorPlus2的defer函数调用,此时再从recover中获取error,error已经在上个defer中获取完毕, 此时在获取error就为nil了;也就只会执行DoNotError的回调函数了。所以此时输出“Defer Func 2”;

最后还有Func1的defer函数调用; 和第二个一样,前面的panic已经获取过了,这里recover的error亦然为nil;所以输出“Defer Func 1”;

recover中的panic

如果在回调里加入panic会是怎样了, 我们改下代码再来试试

func TestDeferFun10(t *testing.T) {
    defer processErrorPlus2(func(err error) {
        fmt.Printf("Defer Func 1 error: %v\n", err)
    }, func() {
        fmt.Printf("Defer Func 1 \n")
    })
​
    defer processErrorPlus2(func(err error) {
        panic(fmt.Sprintf("Func 2 panic: %v", err))
    }, func() {
        fmt.Printf("Defer Func 2 \n")
    })
​
    defer processErrorPlus2(func(err error) {
        panic(fmt.Sprintf("Func 3 panic: %v", err))
    }, func() {
        fmt.Printf("Defer Func 3 \n")
    })
​
    fmt.Println("Test")
​
    panic("This is a testing")
}
 

这段代码对比上面的TestDeferFun9代码,我们来看看,在Func3和Func2的defer processErrorPlus2有些差别; Func3和Func2这里不是简单的打印出error了,而是把recover获取到的error包装成新的error,继续通过panic进行声明;这个操作类似于java中的,在try catch中获取到了exception后,再次通过throw进行exception的上抛。 结果会是如何,让我们来看看执行的结果:

=== RUN   TestDeferFun10
Test
Defer Func 1 error: Func 2 panic: Func 3 panic: This is a testing
--- PASS: TestDeferFun10 (0.00s)
PASS

结束语

通过这两遍介绍defer和recover的文章, 我们可以使用defer和recover的方式,完全的实现java里的exception处理的效果。

相关文章:

  • SQL Dblink SQL
  • 超级无敌详细使用ubuntu搭建hadoop完全分布式集群
  • Flink学习22:窗口的划分
  • 【卫朋】产品管理:如何做缺陷(漏洞)管理?
  • 第11讲:DQL数据查询语句综合案例实战
  • PostgreSQL中的技术内幕
  • MHA高可用
  • 记录VSCode C++网络编程 编译失败出现 undefined reference to _imp_socket等等
  • 记一次升级maven的坑(idea 2021.3.2; maven3.5.0升级3.8.5)
  • 微前端——qiankun(乾坤)实例
  • 设置JVM的内存大小
  • 3D感知技术(4)双目立体视觉测距
  • 孙卫琴的《精通JPA与Hibernate》的读书笔记: 用JPQL批量处理数据
  • linux常用小知识点记录
  • 91.(leaflet之家)leaflet态势标绘-进攻方向绘制
  • [译]CSS 居中(Center)方法大合集
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • 【技术性】Search知识
  • 【知识碎片】第三方登录弹窗效果
  • bootstrap创建登录注册页面
  • go append函数以及写入
  • Koa2 之文件上传下载
  • spring-boot List转Page
  • SpringCloud集成分布式事务LCN (一)
  • Terraform入门 - 3. 变更基础设施
  • TypeScript实现数据结构(一)栈,队列,链表
  • vue-router 实现分析
  • Windows Containers 大冒险: 容器网络
  • Zsh 开发指南(第十四篇 文件读写)
  • 动态规划入门(以爬楼梯为例)
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 今年的LC3大会没了?
  • 理解在java “”i=i++;”所发生的事情
  • 设计模式(12)迭代器模式(讲解+应用)
  • 深入浅出Node.js
  • 什么是Javascript函数节流?
  • 想晋级高级工程师只知道表面是不够的!Git内部原理介绍
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 运行时添加log4j2的appender
  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • # Java NIO(一)FileChannel
  • $.ajax,axios,fetch三种ajax请求的区别
  • (env: Windows,mp,1.06.2308310; lib: 3.2.4) uniapp微信小程序
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (原創) 是否该学PetShop将Model和BLL分开? (.NET) (N-Tier) (PetShop) (OO)
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • .gitignore
  • .gitignore文件---让git自动忽略指定文件
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .NET CF命令行调试器MDbg入门(一)
  • .Net Core 中间件验签
  • .NET中的十进制浮点类型,徐汇区网站设计
  • 。Net下Windows服务程序开发疑惑
  • /etc/shadow字段详解
  • @Transaction注解失效的几种场景(附有示例代码)