# Day17 Go-拖延症候群 (defer) ## 推遲、延後 `defer` 是延遲執行的意思。 > A defer statement defers the execution of a function until the surrounding function returns. 推遲執行`defer的敘述式`,盡可能地拖拖拖等等等,直到目前所在的func打烊結束要回傳時才做事。 這種非得等到最後一刻才去做的精神,和大眾你我一樣很明顯都是拖延病患者,非常的親民。 像我大概就被植入了`defer 發文.寫稿(IT鐵人賽)`這樣子的程式。 #### 【defer】 來看看只有兩行的程式。 ```go func main() { defer fmt.Println("退出main才執行") fmt.Println("Hi") } /* result: Hi 退出main才執行 */ ``` fmt.Println() ```go var a = 50 func main() { defer add_a() fmt.Println(a) } func add_a() { a += 100 } /* result: 50 */ ``` https://play.golang.org/p/VRSQqf1_Bly 疑?奇怪,這樣子懶惰到叫人心寒的函式可以用在哪呢? 世上有哪間公司會雇用懶人嗎? 有的話請務必找我過去。 程式會用到`defer`主要是需要`拖延的時效性`,類似鬧鐘能做到提醒的作用。 > 在開燈的時候, > `defer` 最後出去的人要關燈 把讀檔與關檔寫在一起,這樣比較不容易忘記要關。 ```go f, err := os.Open("test.txt") if err != nil { return err } defer f.Close() ``` 也常運用在`關閉channel` 及 `關閉DB`之中。 ```go db, err := sql.Open("mysql", "USER:PASSWORD@/DB") if err != nil { log.Fatalln(err) } defer db.Close() ``` 一切看似非常地正常、優雅且美好。 ## 【defer的一些小坑】 兩個defer執行的優先順序, 為了要**貫徹拖延的行為**,越早分派的任務要越晚達成才行 => 每日拖延(1/1)✔️。 ### 【defer前後順序】 https://play.golang.org/p/IFgv-lt3Y7Z ```go func main() { defer print1() defer print2() } func print1() { fmt.Println("p1") } func print2() { fmt.Println("p2") } /* result: p2 p1 */ ``` ### 【修改 defer中的參數】 `defer`印出的值是多少,更動前的值?還是更動後? ```go func main() { assign2(50) } func assign2(a int) int { defer fmt.Println(a) // 任務交代下來的時候 a值是50,然後盡可能地拖延 a = 100 // 老闆更動了a為100 return a } /* result: 50 */ ``` ```go func main() { assign1(50) } func assign1(a int) int { a = 100 // 老闆更動了a為100 defer fmt.Println(a) // 任務交代下來的時候 a值是100,然後盡可能地拖延 return a } /* result: 100 */ ``` 全看上頭交代時的參數 https://play.golang.org/p/MYCoWj7Womg > The deferred call's arguments are evaluated immediately, > but the function call is not executed until the surrounding function returns. ### 【當 defer遇上 os.Exit()】 https://play.golang.org/p/2ihaISoyjbd `defer`總想等到店裡打烊時再做行動, 但是如果直接倒店`os.Exit`.. ```go func main() { defer func() { fmt.Println("我很懶,想等到退出func的時候再印東西") }() os.Exit(0) // func直接被砍了 } /* result: */ ``` ### 【匿名函式裡面的defer】 ```go func main() { fmt.Println(func1()) } func func1() int { // 沒有宣告回傳哪個變數 var a int defer func() { a = 100 }() return a // defer:『喔 要回傳a了喔,可是func還沒退出所以我不想做事、我很懶,所以我不會做匿名func內的事。反正回上司也沒有規定要回傳哪個a。』 } /* result: 0 */ ``` ```go func main() { fmt.Println(func2()) } func func2() (a int) { // 注意此行有宣告回傳變數a defer func() { a = 100 }() return a //defer:『蛤,要回傳了喔?雖然很懶、想擺爛,但上司一開始指名規定要回傳a,但a中間被操作了,所以我必須回傳操作過後的...只能趕一下進度了。』 } /* result: 100 */ ``` https://play.golang.org/p/AE9WS1XOGjo **『自始至終,就是想拖』—— Golang.Defer**