Defer - Golang




Welcome to part 20 of the Go programming tutorial, where we will be introducing the defer statement and its use in Go.

The idea of the defer statement is to put off (defer) the execution of something until the surrounding function is done. The defer statement will be evalutated immediately, but wont run until the end, so you can, and usually will, put the defer statement early in the function. The main idea here is to do this to ensure things like files are closed and connections are terminated. You can write your code to do these things at the end of a function, but, consider what can happen if you hit an error, or something goes wrong. The file might not actually ever be closed or the connection terminated. In our case, we wanted to talk about defer because we have a "wait group" for our goroutines, and we wanted to be certain we tell this group a goroutine is "done," even if something goes wrong during that goroutine. Let's see a basic example:

package main

import "fmt"

func foo() {
  defer fmt.Println("Done!")
  fmt.Println("Doing some stuff, who knows what?")
}

func main() {
  foo()
}

If we run this:

Doing some stuff, who knows what?
Done!

As you can see, despite being written out first, the deferred function call happens last. What if we had two defers? How might that work?

package main

import "fmt"

func foo() {
  defer fmt.Println("Done!")
  defer fmt.Println("Are we done yet?")
  fmt.Println("Doing some stuff, who knows what?")
}

func main() {
  foo()
}

Output here would is:

Doing some stuff, who knows what?
Are we done yet?
Done!

How come? The defer statement works on a "first in last out" structure, so the last defer will also be the first one that runs. To further drive this point home, we could change the foo function to instead be:

func foo() {
  for i := 0; i < 5; i++ {
  	defer fmt.Println(i)
  }
}

Then our output is:

4
3
2
1
0

Alright, getting back to our goroutine and wait group, all we need to do is change:

package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func say(s string) {
	for i:=0; i < 3; i++ {
		time.Sleep(100*time.Millisecond)
		fmt.Println(s)
	}	
	wg.Done()
}

func main() {
	wg.Add(1)
	go say("Hey")
	wg.Add(1)
	go say("there")
	wg.Wait()
}

To now, rather than doing wg.Done() at the end of the say function, we do a defer at the top:

func say(s string) {
	defer wg.Done()
	for i:=0; i < 3; i++ {
		time.Sleep(100*time.Millisecond)
		fmt.Println(s)
	}	
}

Now the full code:

package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func say(s string) {
	defer wg.Done()
	for i:=0; i < 3; i++ {
		time.Sleep(100*time.Millisecond)
		fmt.Println(s)
	}	
}

func main() {
	wg.Add(1)
	go say("Hey")
	wg.Add(1)
	go say("there")
	wg.Wait()
}

Now we're all set! Now, if we're going to talk about defer, we might as well also talk about panic and recover, which is what we'll be talking about next!

The next tutorial:





  • Introduction to the Go Programming Language
  • Go Language Syntax
  • Go Language Types
  • Pointers in Go Programming
  • Simple Web App in Go Programming
  • Structs in the Go Programming Language
  • Methods in Go Programming
  • Pointer Receivers in Go Programming
  • More Web Dev in Go Language
  • Acessing the Internet in Go
  • Parsing XML with Go Programming
  • Looping in Go Programming
  • Continuing our Go Web application
  • Mapping in Golang
  • Mapping Golang sitemap data
  • Golang Web App HTML Templating
  • Applying templating to our Golang web app
  • Goroutines - Concurrency in Goprogramming
  • Synchronizing Goroutines - Concurrency in Golang
  • Defer - Golang
  • Panic and Recover in Go Programming
  • Go Channels - Concurrency in Go
  • Go Channels buffering, iteration, and synchronization
  • Adding Concurrency to speed up our Golang Web Application