Your implementation is pretty much what scheduled jobs/tasks in Go look like. There are cron-esque libraries that give you more control for tasks, but in most cases a simple goroutine with a loop is sufficient.
Here are a few more examples in increasing complexity based on what your needs are:
Run a task forever, waiting 5 seconds in between each run. Cannot be stopped and doesn't take into account the run time of the task. This is the simplest form and also the most common one.
go func() {
for {
task()
<-time.After(5 * time.Second)
}
}()
Same as before except now there is a channel to stop the task if we ever want to. While your implementation lets you stop the task via the Stop()
method, the goroutine will remain open since the channel is never closed (and therefore will leak memory).
// Includes a channel to stop the task if needed.
quit := make(chan bool, 1)
go func() {
task()
for {
select {
case <-quit:
return
case <-time.After(5 * time.Second):
task()
}
}
}()
// To stop the task
quit <- true
This is the most robust solution of the three. The task can be stopped at any point, and it also takes into account how long the task took to run when it waits to run it again. In the previous examples, if the task took 1 second to run, and you wait an additional 5 seconds, your interval is actually 6 seconds, relative to when the task starts.
This solution really is only applicable to very long running tasks or if it's critical that your tasks are ran at constant intervals. If the tasks must be ran at constant intervals then you need to take into account the fact that time.After()
will wait at LEAST the duration you provide it -- it could end up waiting slightly longer if the CPU is busy with other processes/goroutines.
// Calls `fn` and then waits so the total elapsed time is `interval`
func runAndWait(interval time.Duration, fn func()) {
earlier := time.Now()
fn()
diff := time.Now().Sub(earlier)
<-time.After(interval - diff)
}
quit := make(chan bool, 1)
go func() {
// The total time to run one iteration of the task
interval := 5 * time.Second
for {
select {
case <-quit:
return
default:
runAndWait(interval, task)
}
}
}()