I have a loop that iterates until a job is up and running:
ticker := time.NewTicker(time.Second * 2)
defer ticker.Stop()
started := time.Now()
for now := range ticker.C {
job, err := client.Job(jobID)
switch err.(type) {
case DoesNotExistError:
continue
case InternalError:
return err
}
if job.State == "running" {
break
}
if now.Sub(started) > time.Minute*2 {
return fmt.Errorf("timed out waiting for job")
}
}
Works great in production. The only problem is that it makes my tests slow. They all wait at least 2 seconds before completing. Is there anyway to get time.Tick
to tick immediately?
The actual implementation of Ticker
internally is pretty complicated. But you can wrap it with a goroutine:
func NewTicker(delay, repeat time.Duration) *time.Ticker {
ticker := time.NewTicker(repeat)
oc := ticker.C
nc := make(chan time.Time, 1)
go func() {
nc <- time.Now()
for tm := range oc {
nc <- tm
}
}()
ticker.C = nc
return ticker
}
If you want to check the job right away, don't use the ticker as the condition in the for loop. For example:
ticker := time.NewTicker(time.Second * 2)
defer ticker.Stop()
started := time.Now()
for {
job, err := client.Job(jobID)
if err == InternalError {
return err
}
if job.State == "running" {
break
}
now := <-ticker.C
if now.Sub(started) > 2*time.Minute {
return fmt.Errorf("timed out waiting for job")
}
}
If you do still need to check for DoesNotExistError
, you want to make sure you do it after the ticker so you don't have a busy-wait.
ticker := time.NewTicker(period)
for ; true; <-ticker.C {
...
}
Unfortunately, it seems that Go developers will not add such functionality in any foreseeable future, so we have to cope...
There are two common ways to use tickers:
for
loopGiven something like this:
for <- time.Tick(period) {
...
}
Use:
for ; true; <- time.Tick(period) {
...
}
for
-select
loopGiven something like this:
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
loop:
for {
select {
case <- time.Tick(period):
f()
case <- interrupt:
break loop
}
}
Use:
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
loop:
for {
f()
select {
case <- time.Tick(period):
continue
case <- interrupt:
break loop
}
}