github.com/qiniu/go-sdk/v7@v7.27.0/sandbox/poll.go (about) 1 package sandbox 2 3 import ( 4 "context" 5 "time" 6 ) 7 8 // PollOption 配置轮询行为的选项。 9 type PollOption func(*pollOpts) 10 11 type pollOpts struct { 12 interval time.Duration 13 maxInterval time.Duration 14 backoff float64 // 退避倍数,默认 1.0(无退避) 15 onPoll func(attempt int) 16 } 17 18 func defaultPollOpts(defaultInterval time.Duration) *pollOpts { 19 return &pollOpts{ 20 interval: defaultInterval, 21 maxInterval: 0, 22 backoff: 1.0, 23 } 24 } 25 26 // WithPollInterval 设置轮询间隔。 27 func WithPollInterval(d time.Duration) PollOption { 28 return func(o *pollOpts) { o.interval = d } 29 } 30 31 // WithBackoff 设置指数退避倍数和最大间隔。 32 // multiplier 为每次轮询后间隔的乘数(如 1.5 表示每次增加 50%), 33 // maxInterval 为间隔上限(0 表示不限制)。 34 func WithBackoff(multiplier float64, maxInterval time.Duration) PollOption { 35 return func(o *pollOpts) { 36 o.backoff = multiplier 37 o.maxInterval = maxInterval 38 } 39 } 40 41 // WithOnPoll 设置每次轮询时的回调函数。 42 // attempt 从 1 开始递增。 43 func WithOnPoll(fn func(attempt int)) PollOption { 44 return func(o *pollOpts) { o.onPoll = fn } 45 } 46 47 // pollLoop 是 WaitForReady 和 WaitForBuild 共享的轮询循环。 48 // pollFn 在每次轮询时被调用,返回 (done, result, error)。 49 func pollLoop[T any](ctx context.Context, opts *pollOpts, pollFn func() (bool, T, error)) (T, error) { 50 if opts.interval <= 0 { 51 opts.interval = time.Second 52 } 53 54 interval := opts.interval 55 var timer *time.Timer 56 defer func() { 57 if timer != nil { 58 timer.Stop() 59 } 60 }() 61 62 attempt := 0 63 for { 64 attempt++ 65 if opts.onPoll != nil { 66 opts.onPoll(attempt) 67 } 68 69 done, result, err := pollFn() 70 if err != nil { 71 return result, err 72 } 73 if done { 74 return result, nil 75 } 76 77 // 计算下次间隔(退避) 78 if opts.backoff > 1.0 { 79 interval = time.Duration(float64(interval) * opts.backoff) 80 if opts.maxInterval > 0 && interval > opts.maxInterval { 81 interval = opts.maxInterval 82 } 83 } 84 85 if timer == nil { 86 timer = time.NewTimer(interval) 87 } else { 88 timer.Reset(interval) 89 } 90 select { 91 case <-ctx.Done(): 92 var zero T 93 return zero, ctx.Err() 94 case <-timer.C: 95 } 96 } 97 }