Handling Timeouts in Golang: A practical example
Operating with concurrency there are going to be situations where we need to consider a timeout because some process has not occurred and want to execute some logic in such a situation.
Timeouts are useful because you get to handle heartbeats, batch processing and even more practical uses.
So how can we handle timeout in golang.
Well there are a couple of ways to do it.
If you find this article useful please consider giving a few claps and subscribing to encourage me to write better articles.
Using time.After
This is a simple method where we can run a function after some time period using the time module.
select {
case <-time.After(5 * time.Second):
fmt.Println("Operation timed out")
case result := <-resultChan:
fmt.Println("Received result:", result)
}
After 5 seconds the first case runs and Operation timed out comes on screen.
The time.AfterFunc
This is a cool method where we define what function we wish to run after a specified duration of time.
timer := time.AfterFunc(5*time.Second, func() {
fmt.Println("Timeout occurred")
// Cancel the operation or take other actions
})
defer timer.Stop()
After 5 seconds the function executes.
Using time.Ticker
We use a channel where it sends values at regular intervals
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
fmt.Println("Tick")
case <-doneChan:
return
}
}
When we define the ticker it means after every 1 second it sends some value to the channel so the first case is triggered then. This is super useful in case we wish to do heartbeats.
A simple Example
Lets run a simple example which shows how they work.
package main
import (
"fmt"
"math/rand"
"time"
)
// Function to execute on timeout
func Reload() {
fmt.Println("Executed function on timeout")
}
// Function to simulate timeout using time.After
func timeoutWithAfter() {
start := time.Now()
timeoutDuration := time.Duration(150+rand.Intn(150)) * time.Millisecond
fmt.Printf("\ntime.After timeout duration: %v\n", timeoutDuration)
select {
case <-time.After(timeoutDuration):
fmt.Printf("Elapsed time with time.After: %v\n", time.Since(start))
Reload()
}
}
// Function to simulate timeout using time.AfterFunc
func timeoutWithAfterFunc() {
start := time.Now()
timeoutDuration := time.Duration(150+rand.Intn(150)) * time.Millisecond
fmt.Printf("\ntime.AfterFunc timeout duration: %v\n", timeoutDuration)
time.AfterFunc(timeoutDuration, func() {
fmt.Printf("Elapsed time with time.AfterFunc: %v\n", time.Since(start))
Reload()
})
// Prevent main from exiting immediately
time.Sleep(timeoutDuration + 50*time.Millisecond)
}
// Function to simulate timeout using time.Ticker
func timeoutWithTicker() {
start := time.Now()
timeoutDuration := time.Duration(150+rand.Intn(150)) * time.Millisecond
fmt.Printf("\ntime.Ticker timeout duration: %v\n", timeoutDuration)
ticker := time.NewTicker(timeoutDuration)
defer ticker.Stop()
select {
case <-ticker.C:
fmt.Printf("Elapsed time with time.Ticker: %v\n", time.Since(start))
Reload()
}
}
// Main function to run all timeout methods
func main() {
rand.Seed(time.Now().UnixNano())
fmt.Println("Starting the timeout demonstrations...\n")
// Demonstrate timeout using time.After
timeoutWithAfter()
// Demonstrate timeout using time.AfterFunc
timeoutWithAfterFunc()
// Demonstrate timeout using time.Ticker
timeoutWithTicker()
fmt.Println("All timeout demonstrations complete.")
}
On running this we can see
It shows the timeout value we have configured and exactly how much time later the function executes.
Like such content , follow for more.