In programming we need specifying time for our code. Sometimes we wait until our crush end her relationship. Sometimes we wait until summer to change our job.
Let's Start!
For doing thing with specified time I will show the basics in Go. In the first part we will look 2 simple code part. After we will discuss about how can we improve them and make it usable even production environment.
Part 1 — Basics Of Time Specifying in Go
For time specifying we will use 2 basic technique. First one is for blocking current goroutine until specified time.
func main() { | |
// This will block for 5 seconds and then return the current time | |
theTime := <-time.After(time.Second * 5) | |
fmt.Println(theTime.Format("2006-01-02 15:04:05")) | |
} |
In this example goroutine blocked for 5 second at 3rd line and continues after this. If you want waiting more time and doing more readable. You can use time.Until()
. Here a basic example for this.
func main() { | |
// Set when we want to continue | |
until, _ := time.Parse(time.RFC3339, "2023-01-01T00:00:01+02:00") | |
// Wait until it | |
<-time.After(time.Until(until)) | |
} |
Second technique is blocking goroutine forever and make it cron engine. For doing this we have an excellent function called time.Tick()
. This method takes duration and returns channel that triggers at every duration loop.
func main() { | |
// This will print the time every 5 seconds | |
for theTime := range time.Tick(time.Second * 5) { | |
fmt.Println(theTime.Format("2006-01-02 15:04:05")) | |
} | |
} |
If you don’t care about current time and just want to execute code continuously you can use this one. Also this way is more manageable and allow control flow with context or specific trigger channels.
func main() { | |
for { | |
select { | |
// This usage will ignore actual time | |
// Just runs in every 5 second | |
case <-time.Tick(time.Second * 5): | |
fmt.Println("I'm a bot that saying 'You are amazing' in every 5 second") | |
} | |
} | |
} |
Part 2 — Let’s Improve Them With Context Package
Context package is so useful. Because Context gives you a power of cancellation. You can cancel functions with it. If you are doing things with time, context is like Thor’s Mjölnir for you.
Key part of using context is based on for { select{…} }
loop. Also you must use async for be able to use context. Because If your code flow don’t run, you can’t cancel the context.
Let’s do some examples. First we will implement a basic wait function with context. If context cancel called before timer end, function will exit without wait to timer end. In that case our program just wait 5 second instead of 5 minutes. That can annoy your goroutine :)
func main() { | |
ctx, cancel := context.WithCancel(context.Background()) | |
go func() { | |
fmt.Println("Let's wait 5 minute") | |
WaitUntilWithContext(ctx, time.Minute*5) | |
fmt.Println("Hey!! Why we exited after 5 second !?!??!") | |
}() | |
time.Sleep(5 * time.Second) | |
cancel() | |
time.Sleep(10 * time.Second) | |
} | |
func WaitUntilWithContext(ctx context.Context, diff time.Duration) { | |
timer := time.NewTimer(diff) | |
defer timer.Stop() | |
select { | |
case <-timer.C: | |
return | |
case <-ctx.Done(): | |
return | |
} | |
} |
For key point. We split our waiting routine and main routine. We must do that because if we didn’t do that we can’t call cancel function asynchronously.
Creating cron engine with context is basic than previous example. Just add context.Done()
to the select statement.
func main() { | |
ctx, cancel := context.WithCancel(context.Background()) | |
go func() { | |
for { | |
select { | |
// This usage will ignore actual time | |
// Just runs in every 5 second | |
case <-ctx.Done(): | |
return | |
case <-time.Tick(time.Second * 5): | |
fmt.Println("I'm a bot that saying 'You are amazing' in every 5 second") | |
} | |
} | |
}() | |
time.Sleep(11 * time.Second) | |
cancel() | |
time.Sleep(5 * time.Second) | |
} |
For a final example I want to add more complex, beautiful and useful cron engine example from here. I won’t add code to article. You can use link for this. Also this repository is a great implementation and my inspiration for writing this article.
I would like to hear all feedback’s about this article. Please feel free to give feedback. I want to hear all of good and bad feedbacks :)
I’m planning to expand this article some. If you have an idea what can I talk about at the feature of this article, I want to hear it.
Inspired Articles
Good Repositories
Top comments (0)