How can I make this simple for
loop break after exactly one 1s has passed since its execution?
var i int
for {
i++
}
By checking the elapsed time since the start:
var i int
for start := time.Now(); time.Since(start) < time.Second; {
i++
}
Or using a "timeout" channel, acquired by calling time.After()
. Use select
to check if time is up, but you must add a default
branch so it will be a non-blocking check. If time is up, break from the loop. Also very important to use a label and break from the for
loop, else break
will just break from the select
and it will be an endless loop.
loop:
for timeout := time.After(time.Second); ; {
select {
case <-timeout:
break loop
default:
}
i++
}
Note: If the loop body also performs communication operations (like send or receive), using a timeout channel may be the only viable option! (You can list the timeout check and the loop's communication op in the same select
.)
We may rewrite the timeout channel solution to not use a label:
for stay, timeout := true, time.After(time.Second); stay; {
i++
select {
case <-timeout:
stay = false
default:
}
}
I know your loop is just an example, but if the loop is doing just a tiny bit of work, it is not worth checking the timeout in every iteration. We may rewrite the first solution to check timeout e.g. in every 10 iterations like this:
var i int
for start := time.Now(); ; {
if i % 10 == 0 {
if time.Since(start) > time.Second {
break
}
}
i++
}
We may choose an iteration number which is a multiple of 2, and then we may use bitmasks which is supposed to be even faster than remainder check:
var i int
for start := time.Now(); ; {
if i&0x0f == 0 { // Check in every 16th iteration
if time.Since(start) > time.Second {
break
}
}
i++
}
We may also calculate the end time once (when the loop must end), and then you just have to compare the current time to this:
var i int
for end := time.Now().Add(time.Second); ; {
if i&0x0f == 0 { // Check in every 16th iteration
if time.Now().After(end) {
break
}
}
i++
}
I know the question is a bit old, but below might be useful for someone looking for similar scenario:
func keepCheckingSomething() (bool, error) {
timeout := time.After(10 * time.Second)
ticker := time.Tick(500 * time.Millisecond)
// Keep trying until we're timed out or get a result/error
for {
select {
// Got a timeout! fail with a timeout error
case <-timeout:
return false, errors.New("timed out")
// Got a tick, we should check on checkSomething()
case <-ticker:
ok, err := checkSomething()
if err != nil {
// We may return, or ignore the error
return false, err
// checkSomething() done! let's return
} else if ok {
return true, nil
}
// checkSomething() isn't done yet, but it didn't fail either, let's try again
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With