---
# System prepended metadata

title: Day17 Go-拖延症候群 (defer)

---

# 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**  