91 lines
2.0 KiB
Go
91 lines
2.0 KiB
Go
package async
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
defaultInterval = time.Second
|
|
defaultTimeout = time.Minute * 5
|
|
)
|
|
|
|
type IntervalStrategy func() <-chan time.Time
|
|
|
|
// WaitSyncConfig defines the waiting options.
|
|
type WaitSyncConfig struct {
|
|
// This method will be called from another goroutine.
|
|
Get func() (value interface{}, isTerminal bool, err error)
|
|
IntervalStrategy IntervalStrategy
|
|
Timeout time.Duration
|
|
}
|
|
|
|
// LinearIntervalStrategy defines a linear interval duration.
|
|
func LinearIntervalStrategy(interval time.Duration) IntervalStrategy {
|
|
return func() <-chan time.Time {
|
|
return time.After(interval)
|
|
}
|
|
}
|
|
|
|
// FibonacciIntervalStrategy defines an interval duration who follow the Fibonacci sequence.
|
|
func FibonacciIntervalStrategy(base time.Duration, factor float32) IntervalStrategy {
|
|
var x, y float32 = 0, 1
|
|
|
|
return func() <-chan time.Time {
|
|
x, y = y, x+(y*factor)
|
|
return time.After(time.Duration(x) * base)
|
|
}
|
|
}
|
|
|
|
// WaitSync waits and returns when a given stop condition is true or if an error occurs.
|
|
func WaitSync(config *WaitSyncConfig) (terminalValue interface{}, err error) {
|
|
// initialize configuration
|
|
if config.IntervalStrategy == nil {
|
|
config.IntervalStrategy = LinearIntervalStrategy(defaultInterval)
|
|
}
|
|
|
|
if config.Timeout == 0 {
|
|
config.Timeout = defaultTimeout
|
|
}
|
|
|
|
resultValue := make(chan interface{})
|
|
resultErr := make(chan error)
|
|
timeout := make(chan bool)
|
|
|
|
go func() {
|
|
for {
|
|
// get the payload
|
|
value, stopCondition, err := config.Get()
|
|
|
|
// send the payload
|
|
if err != nil {
|
|
resultErr <- err
|
|
return
|
|
}
|
|
if stopCondition {
|
|
resultValue <- value
|
|
return
|
|
}
|
|
|
|
// waiting for an interval before next get() call or a timeout
|
|
select {
|
|
case <-timeout:
|
|
return
|
|
case <-config.IntervalStrategy():
|
|
// sleep
|
|
}
|
|
}
|
|
}()
|
|
|
|
// waiting for a result or a timeout
|
|
select {
|
|
case val := <-resultValue:
|
|
return val, nil
|
|
case err := <-resultErr:
|
|
return nil, err
|
|
case <-time.After(config.Timeout):
|
|
timeout <- true
|
|
return nil, fmt.Errorf("timeout after %v", config.Timeout)
|
|
}
|
|
}
|