一、defer呼叫
介紹:
確保在函式呼叫結束時發生
defer就是在程式或函式呼叫結束後,然後執行defer後面的語句
例如:
package main
import "fmt"
func tryDefer(){
fmt.Println(1)
fmt.Println(2)
fmt.Println(3)
//列印:1、2、3
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
//列印:3、2、1
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
return
fmt.Println(4)
//列印:3、2、1
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
panic("error occurred")
fmt.Println(4)
//列印:3、2、1
//panic: error occurred
}
func mian(){
tryDefer()
}
- 在這個例子第二個列印中,前面加上
defer
關鍵字後,其後面的 fmt.Println(1)和 fmt.Println(2)都會該程式所有語句執行結束後執行,而defer裡面本身是一個棧(先進後出),所有會先列印2,然後列印1。 - 在這個例子的第三個列印中,我們加了關鍵字
return
其輸出結果還是1、2、3,這裡是因為return後程式就會退出,當程式退出前defer仍然會執行 - 在這個例子的第四個列印中,我們使用到函式
panic()
程式會報錯退出,但是在結束之前仍然會執行defer
還有一個例子:
func writeFile(filename string){
file, err := os.Create(filename) //os.Create()建立檔案
if err != nil{ //err不為空,則檔案建立失敗,panic輸出err資訊
panic(err)
}
defer File.close() //最後將檔案關閉
writer := bufio.NerWriter(file)
defer writer.Flush()
f := fibonacci()
for i := 0; i < 20; i++{
fmt.Fprintln(writer, f())
}
}
func fibonacci() func() int {
a := 0, b := 1
return func() int{
a, b = b, a+b
return a
}
}
func mian(){
writerFile("fib.txt")
}
二、錯誤處理
前面我們提到了panic()
函式其實它也是用來做錯誤處理的,但是,使用panic
程式會掛掉,這是我們不希望看到的。
file, err := os.Open("abc.txt")
if err != nil{
panic(err)
}
像上面程式碼使用panic後程式就會把err中的內容輸出,並且掛掉
file, err := os.Open("abc.txt")
if err != nil{
pathError, ok := err.(*os.PathError)
if !ok{
fmt.Println(pathError)
}else {
fmt.Println("unknown error ", err)
}
}
使用這種處理方式更好
三、panic&recover
- panic
2.recover
package main
import "fmt"
func tryRecover(){
defer func(){
r := recover()
err, ok := r.(error)
if ok{
fmt.Println("Error occurred:", err)
}else{
panic("I don`t kown what to : %v", r)
}
}()
panic("123")
}
func mian(){
tryRecover()
}
四、統一處理
伺服器統一出錯:
web.go:
package main
import (
"learngo/errhandling/filelistingserver/handle"
"log" "net/http" "os")
//定義一個函式型別
type Apphandle func(write http.ResponseWriter,
request *http.Request) error
//函數語言程式設計,傳入一個函式Apphandle型別的函式型別,返回一個匿名函式func(http.ResponseWriter,*http.Request)
func errwarappler(handler Apphandle) func(http.ResponseWriter, *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) {
//匿名函式func(http.ResponseWriter,*http.Request)內部構造:
defer func(){
r := recover()
if r != nil{
log.Printf("panic:%v\n", r)
http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
}()
err := handler(writer, request)
if err != nil{
log.Printf("Error handle request:%s", err.Error())
if userErr, ok := err.(userError); ok{
http.Error(writer, userErr.Massage(), http.StatusBadRequest)
return
}
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
Massage() string
}
func main() {
//傳入部分
http.HandleFunc("/",errwarappler(handle.Handler))
err := http.ListenAndServe(":8888" , nil)
if err != nil{
panic(err)
}
}
handle.go:
package handle
import (
"fmt"
"io/ioutil" "net/http" "os" "strings")
const perfix = "/list/"
type userError string
func (e userError)Error() string{
return e.Massage()
}
func (e userError)Massage() string {
return string(e)
}
func Handler(write http.ResponseWriter, Request *http.Request) error{
if strings.Index(Request.URL.Path, perfix) != 0 {
return userError(fmt.Sprintf("path %s must start" +
"whit %s",Request.URL.Path, perfix))
}
path := Request.URL.Path[len(perfix):]
file , err := os.Open(path)
if err != nil{
//將錯誤資訊返回出去
return err
}
defer file.Close()
all, err := ioutil.ReadAll(file)
if err != nil{
return err
}
write.Write(all)
return nil
}
注意:error和panic
在意料之外的情況:使用panic
在意料之內的情況:使用error
本作品採用《CC 協議》,轉載必須註明作者和本文連結