codebyomar

Go nuggets: Go code execution with time.Sleep

May 31, 2020

Alt text This is the first Go nuggets series post. The Go nuggets series is my way of sharing pieces of the go language i find very interesting. As the word "nuggets" suggest the posts are going to be quite short. Let's dive right in.
At some point in time we want to execute some code in the future, either once, repeatedly or at some interval. The golang time package provides us with all of that. I will focus on the time.Sleep() function in this post.

time.Sleep()

time.Sleep() snoozes the current goroutine for the specified duration. It accepts a parameter of type time.Duration

package main

import (
	"fmt"
	"time"
)

func preparePizza() {
  // sleep for 3 seconds
	time.Sleep(3*time.Second)
	fmt.Println("here is your pizza 🍕")
}

func main() {
  // pizza is getting prepared
  fmt.Println("Hold on, your pizza is getting prepared")

  // go ahead and prepare the pizza
  preparePizza()

  // accept the next order
  fmt.Println("Next order please...")

  // the code below is just to prevent the code from exiting until after we finish preparing our pizza
  time.Sleep(5*time.Second)
  fmt.Println("Restaurant closed 👋")
}

By calling the preparePizza() function we pause the code execution. If you run the code above you get the following result:

Hold on, your pizza is getting prepared
here is your pizza 🍕
Next order please...
Restaurant closed 👋

The first line would be printed immediately, simulating that a pizza order has been accepted, and then the program pauses for 3 seconds, again simulating the pizza is getting prepared. After 3 seconds the next two lines would be printed suggesting our pizza has been prepared and we are ready to take the next order.
Let's say our fake restaurant has so many customers waiting in line, it'll be too slow to wait till we are done preparing one order before accepting the next. Fortunately we can unblock the code execution using goroutines.

// main.go

customers := []int{1,2,3}

for _, customer := range customers {
  // pizza is getting prepared
  fmt.Printf("Hold on customer %v, your pizza is getting prepared\n", customer)

  // go ahead an prepare the pizza
  go preparePizza(customer)

  // accept the next order
  fmt.Println("Next order please...")
}

// end main.go

// modified function

func preparePizza(customer int) {
  // sleep for 3 seconds
  time.Sleep(3*time.Second)
  fmt.Printf("here is your pizza 🍕, customer %v\n", customer)
}

// end of modified function

We have modified our code to accept orders from three customers. To invoke our preparePizza function in a new goroutine, we use go keyword in front of it. Run the code and you'll notice the prepare pizza function isn't blocking the code execution anymore. Our fake restaurant can now accept a new order before it's done preparing subsequent orders. Results:

Hold on customer 1, your pizza is getting prepared
Next order please...
Hold on customer 2, your pizza is getting prepared
Next order please...
Hold on customer 3, your pizza is getting prepared
Next order please...
here is your pizza 🍕, customer 1
here is your pizza 🍕, customer 2
here is your pizza 🍕, customer 3
Restaurant closed 👋

Because goroutines execute concurrently your code might print a different result.

To ensure your code prints the same exact result as mine you can pause code execution before taking the next order, giving the previous goroutine a head start.

    // accept the next order
    fmt.Println("Next order please...")
    time.Sleep(1*time.Second)

Umar Abdullahi

Personal blog by Umar Abdullahi

Get things done with mordern technologies.