Channels, Timeouts, and Tickers

This lesson explains how to set the responses of goroutines with time as a controlling factor.

The time package has some interesting functionality to use in combination with channels. It contains a struct time.Ticker, which is an object that repeatedly sends a time value on a contained channel C at a specified time interval:

type Ticker struct {
  C <-chan Time // the channel on which the ticks are delivered.
  // contains filtered or unexported fields
  ...
}

A Ticker can be very useful when, during the execution of goroutines, something (logging a status, a printout, a calculation, and so on) has to be done periodically at a certain time interval. It is stopped with Stop(); use this in a defer statement. All this fits nicely in a select statement:

ticker := time.NewTicker(updateInterval)
defer ticker.Stop()
...
select {
  case u:= <- ch1:
    ...
  case v:= <- ch2:
    ...
  case <- ticker.C:

   logState(status) // e.g. call some logging function logState

  default: // no value ready to be received
    ...
}

The time.Tick() function with signature func Tick(d Duration) <-chan Time is useful when you only need access to the return channel and don’t need to shut it down. It sends out the time on the return channel with periodicity d, which is a number of nanoseconds. It is handy to use when you have to limit the rate of processing per unit time like in the following code snippet (the function client.Call( ) is an RPC-call, the details are not further specified here):

import "time"

rate_per_sec := 10
var dur Duration = 1e9 / rate_per_sec
chRate := time.Tick(dur) // a tick every 1/10th of a second
for req := range requests {
  <- chRate // rate limit our Service.Method RPC calls
  go client.Call("Service.Method", req, ...)
}

The net effect is that new requests are only handled at the indicated rate, which means the channel chRate blocks higher rates. The rate per second can be increased or decreased according to the load and / or the resources of the machine.

A tick type looks exactly the same as a Ticker type (it is constructed with time.Tick(d Duration)), but it sends the time only once, after a Duration d. There is also a function time.After(d) with the signature: func After(d Duration) <-chan Time. After Duration d, the current time is sent on the returned channel; this is equivalent to NewTimer(d).C It resembles Tick(), but After() sends the time only once. The following listing shows a concrete example, and also nicely illustrates the default clause in select:

Get hands-on with 1200+ tech skills courses.