defer 调用

确保调用在函数结束时发生,从下面的例子我们可以发现,defer的列表为先进后出

// 3
// 2
// 1
// panic: error
func tryDefer() {
    defer fmt.Println(1)
    defer fmt.Println(2)
    fmt.Println(3)
    panic("error")
    fmt.Println(4)
}

参数在defer语句时进行计算

// 3
// 2
// 1
func tryDefer() {
    for i := 0; i < 10; i++ {
        defer fmt.Println(i)
        if(i == 3) {
            panic("printed too many")
        }
    }
}

错误统一处理

下面以一个http web server 的例子来叙述如何对错误进行统一处理

package main

type appHandler func(writer http.ResponseWriter, request *http.Request) error

func errWrapper(handler appHandler) func(http.ResponseWriter, *http.Request)  {

    return func(writer http.ResponseWriter, request *http.Request) {

        err := handler(writer, request)
        if err != nil {

            log.Warn("error %s", err.Error())

            code := http.StatusOK

            switch {
            case os.IsNotExist(err):
                code = http.StatusNotFound

            case os.IsPermission(err):
                code = http.StatusForbidden

            default:
                code = http.StatusInternalServerError
            }

            http.Error(writer, http.StatusText(code), code)
        }
    }
}

func main() {

    http.HandleFunc("/list/", errWrapper(filelisting.HandleFileList))

    err := http.ListenAndServe(":12345", nil)

    if err != nil {
        panic(err)
    }
}
package filelisting

func HandleFileList(writer http.ResponseWriter, request *http.Request) error {

    path := request.URL.Path[len("/list/"):]

    file, err := os.Open(path)

    if err != nil {
        return err
    }

    defer file.Close()
    all, err := ioutil.ReadAll(file)

    if err != nil {
        return err
    }

    writer.Write(all)
    return nil
}

panic

  • 停止当前函数执行
  • 一直向上返回,执行每一层的defer
  • 如果没有遇见revocer,程序退出

revocer

  • 仅在defer 调用中使用
  • 获取panic 的值
  • 如果无法处理可重新panic

revocer 的使用

func tryRecover() {
    defer func() {
        r := recover()
        if err, ok := r.(error); ok {
            fmt.Println("Error occurred: ", err)
        } else {
            panic(fmt.Sprintf("I dont know that to do: %v", r))
        }
    }()

    panic(errors.New("this is an error")) 
}

func main() {
    tryRecover()
}

What doesn’t kill you makes you stronger.