defer 调用
- 确保调用在函数结束时发生,即使中间发生panic,defer也会被执行;defer的调用顺序是先进后出
package main
import "fmt"
func tryDefer() {
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
panic("error occurred")
fmt.Println(4)
}
func main() {
tryDefer()
}
输出结果:
3
2
1
panic: error occurred
goroutine 1 [running]:
main.tryDefer()
C:/Users/ehuamay/go/src/awesomeProject/8-1-defer/defer/defer.go:9 +0x163
main.main()
C:/Users/ehuamay/go/src/awesomeProject/8-1-defer/defer/defer.go:13 +0x27
Process finished with exit code 2
- 参数在defer语句时计算
- defer列表为后进先出
何时使用defer调用
- Open/Close
- Lock/Unlock
- PrintHeader/PrintFooter
package main
import (
"fmt"
"os"
"bufio"
"awesomeProject/8-1-defer/fibo"
)
func writeFile(filename string) {
file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666)
//err = errors.New("This is a customer error")
if err != nil {
//panic(err)
if pathError, ok := err.(*os.PathError); !ok {
panic(err)
} else {
fmt.Printf("%s, %s, %s\n",
pathError.Op, pathError.Path, pathError.Err)
}
return
}
defer file.Close()
write := bufio.NewWriter(file)
defer write.Flush()
f := fibo.Fibonacci()
for i := 0; i < 20; i++ {
fmt.Fprintln(write, f())
}
}
func main() {
writeFile("fib.txt")
}
如何统一处理err
可以用函数式编程的思想统一处理error
package filelisting
import (
"net/http"
"os"
"io/ioutil"
)
func HandleFileList (writer http.ResponseWriter, request *http.Request) error{
path := request.URL.Path[len("/list/"):]
file, err := os.Open(path)
if err != nil {
/*//panic(err)
http.Error(writer,
err.Error(),
http.StatusInternalServerError)*/
return err
}
defer file.Close()
all, err := ioutil.ReadAll(file)
if err != nil {
panic(err)
}
writer.Write(all)
return nil
}
package main
import (
"net/http"
"awesomeProject/8-3/filelistingserver/filelisting"
"os"
"log"
)
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 handling request:%s", err.Error())
log.Printf("Error occured" +
" handling request:%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(":8000", nil)
if err != nil {
panic(err)
}
}
panic
- 停止当前函数执行
- 一直向上返回,执行每一层的defer
- 如果没有遇到recover,程序退出
recover
- 仅在defer调用中使用
- 获取panic的值
- 如果无法处理,可重新panic
package main
import (
"fmt"
)
func tryRecover() {
defer func() {
r := recover()
if err, ok := r.(error); ok {
fmt.Println("Error occurred:", err)
} else {
panic(fmt.Sprintf("I don't known what to do: %v", r))
}
}()
//panic(errors.New("This is an error"))
/*b := 0
a := 5 / b
fmt.Println(a)*/
panic(123)
}
func main() {
tryRecover()
}
error VS panic
- 意料之中的:使用error。如:文件打不开
- 意料之外:使用panic。如:数组越界
package filelisting
import (
"net/http"
"os"
"io/ioutil"
"strings"
"fmt"
)
const prefix = "/list/"
type userError string
func (e userError) Error() string {
return e.Message()
}
func (e userError) Message() string {
return string(e)
}
func HandleFileList (writer http.ResponseWriter, request *http.Request) error{
if strings.Index(request.URL.Path, prefix) != 0 {
return userError(fmt.Sprintf("path %s must start " +
"with %s", request.URL.Path, prefix))
}
path := request.URL.Path[len(prefix):]
file, err := os.Open(path)
if err != nil {
/*//panic(err)
http.Error(writer,
err.Error(),
http.StatusInternalServerError)*/
return err
}
defer file.Close()
all, err := ioutil.ReadAll(file)
if err != nil {
panic(err)
}
writer.Write(all)
return nil
}
package main
import (
"net/http"
"os"
"log"
"awesomeProject/8-5-unifyerror/filelistingserver/filelisting"
)
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) {
defer func() {
if r := recover(); r != nil{
log.Printf("panic: %v", r)
http.Error(writer,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
}
}()
err := handler(writer, request)
if err != nil {
//log.Warn("Error handling request:%s", err.Error())
log.Printf("Error occured" +
" handling request:%s",
err.Error())
//user error
//userErr, _ := err.(userError)
log.Printf("%v", err)
if userErr, ok := err.(userError); ok {
http.Error(writer,
userErr.Message(),
http.StatusBadRequest)
return
}
//system 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)
}
}
}
type userError interface {
error
Message() string
}
func main() {
http.HandleFunc("/", errWrapper(filelisting.HandleFileList))
err := http.ListenAndServe(":8000", nil)
if err != nil {
panic(err)
}
}