Go Control Flow - Deferring Panic with Recovery
Tags: go
Published: 2021-08-02 07:57:22

## Pre-requisite

---

- I assume that you must have understood the basic of Go Lang.
- I assume that you must have understood how basic control flow, like if-statement, switch, and loop works.
- I advice you to clone my repository.

```bash
git clone https://github.com/codewithdjaeger/go-with-golang.git

cd go-with-golang/control-flow
```

## Control Flow

---

Technically, control flow is the order in which individual statements, instructions, or function calls of an imperative program are executed or evaluated. Simply, it is the order of the lines of the code which contains either functions or statements that will be executed.

![Control Flow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bd5bj6ixzox8lt9oi8du.png)

The most common control flow in almost every language is if-statement, switch, and looping. But, Go also has its signature control flow and they are `defer`, `panic`, and `recovery`. Usually, they're used to handling the error that is occurred in the control flow.

## Defer the Flow Deferrer

---

`defer`, as its name, it is a keyword that will postpone the execution of a function calls until before the function in which the `defer` statement is located returns. Simply, any function calls that is preceded by the `defer` keyword will be fired immediately before the function returns. To make it clearer, take a look at the code below.

```go
package main

import "fmt"

func main() {
	defer fmt.Println("This will be printed at the end")
	fmt.Println("This will be printed at the beginning")
}

// Output : 
// This will be printed at the beginning
// This will be printed at the end

```

Just for you know, `defer` only handle function calls. Otherwise, it will raise an error.

Usually, `defer` is used to help you handle the task that you might forget, like closing a file. For more advanced uses, it will help you to deal with the error. But, we will discuss this later. For now, take a look at the code below to see the example of `defer` use to close the file.

```go
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	res, err := http.Get("http://www.google.com/robots.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close() // Close response body
	robots, err := ioutil.ReadAll(res.Body) // Read response body
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(robots)
}
```

As you can see in the code above, we have close the response body before the program reads it. Normally, it will cause the program to be terminated because the program trying to read on a closed response body. But, thanks to `defer`, the closing of the response body will be postponed until before the main function reaches its return.

So, what if we precede defer in all function calls in order? 

Theoretically, the program will scan and execute all deferred function calls from the last order and it will result in the LIFO (Last In First Out) output.

```go
package main

import "fmt"

func main() {
	defer fmt.Println("This will be printed last")
	defer fmt.Println("This will be printed second")
	defer fmt.Println("This will be printed first")
}

// Output : 
// This will be printed first
// This will be printed second
// This will be printed last
```

## Panic, Makes Me Panic

---

Exception in Go is called `panic`, it can either be thrown by runtime or forced by programmer to handle the worse case. It will terminate current running control flow like the example below.

```go
package main

import "fmt"

func main() {
	fmt.Println("Get ready!")
	panic("LET'S PANIC!!!")
	fmt.Println("This will not be executed")
}

// Output :
// Get ready!
// panic: LET'S PANIC!!!

// goroutine 1 [running]:
// main.main()
//        D:/Code/learn/learn-golang/control flow/panic.go:7 +0xa5
// exit status 2
```

Also, `panic` will fire all `defer` function calls that is declared before the `panic` within the same function.

```go
package main

import "fmt"

func main() {
	defer fmt.Println("This will be printed immediately when panic occurred!")
	fmt.Println("Get ready!")
	panic("LET'S PANIC!!!")
}

// Output :
// Get ready!
// This will be printed immediately when panic occurred!
// panic: LET'S PANIC!!!

// goroutine 1 [running]:
// main.main()
//         D:/Code/learn/learn-golang/control flow/panicDefer.go:8 +0x105
// exit status 2
```

From the code above, we've known that `defer` will still be executed despite the program is terminated by `panic`. It explains why `defer` can be used to deal with errors. But, How? We will cover this in the `recover` function section.

## Recover Will Do the Recovery

---

`recover` helps you to regain control after panicking. It's really useful to clean up the mess caused by the error and prevent your program from just terminating without closing or doing some clean-up task. 

To do it, we can combine the uses of `defer` and `recover` like the example below.

```go
package main

import (
	"fmt"
	"log"
)

func recoveryFunction() {
	if err := recover(); err != nil {
		log.Println("Program Recovered from", err)
	}
}

func panicFunction() {
	defer recoveryFunction()
	fmt.Println("Get Ready!")
	panic("PANIC!!!!")
	fmt.Println("Panic End")
}

func main() {
	fmt.Println("Program Initialized")
	panicFunction()
	fmt.Println("Program end")
}

// Output :
// Program Initialized
// Get Ready!
// 2021/08/02 13:56:36 Program Recovered from PANIC!!!!
// Program end
```

In the above example, we `defer` function that contains `recover` function. From there, we see that the program doesn't throw something like the output below and also does not terminate the program.

```go
// goroutine 1 [running]:
// main.main()
//         D:/Code/learn/learn-golang/control flow/panicDefer.go:8 +0x105
// exit status 2
```

Instead,  it prints a log of what happened and still runs the program until its end. It is good because, in the real world, the program is expected not to be terminated/exit and left the process unfinished when the error occurred.

## Conclusions

---

- Control flow is the order of lines of code that will be executed.
- In Go, there are 3 signatures part of error handling, it is `defer`, `panic`, and `recover`.
- `defer` will Postpone the execution of function calls until before the function return.
- `panic` can either be thrown by runtime error or forced with custom error.
- `recover` can be used to determine how the error will be handled instead of terminating the program.