3 changed files with 130 additions and 1 deletions
@ -0,0 +1,117 @@
|
||||
package promise |
||||
|
||||
import ( |
||||
"errors" |
||||
"sync" |
||||
"time" |
||||
) |
||||
|
||||
var ( |
||||
promiseTicker *time.Ticker |
||||
promiseTicks []*PromiseAll // todo 内存释放 capacity / len > ?
|
||||
promiseTickerLock sync.Mutex |
||||
|
||||
ErrorTimeout error = errors.New("promise timeout") |
||||
NoExpire time.Duration = 0 |
||||
) |
||||
|
||||
func init() { |
||||
promiseTicker = time.NewTicker(100 * time.Millisecond) |
||||
go func() { |
||||
for { |
||||
now := <-promiseTicker.C |
||||
promiseTickerLock.Lock() |
||||
index := 0 |
||||
for _, tick := range promiseTicks { |
||||
if finish := tick.tick(now); !finish { |
||||
promiseTicks[index] = tick |
||||
index++ |
||||
} |
||||
} |
||||
promiseTicks = promiseTicks[:index] |
||||
promiseTickerLock.Unlock() |
||||
} |
||||
}() |
||||
} |
||||
|
||||
type PromiseAll struct { |
||||
stime time.Time |
||||
timeout time.Duration |
||||
subs int |
||||
keys map[string]bool |
||||
data map[string]any |
||||
finallyCall func(data map[string]any, err error) |
||||
sync.Mutex |
||||
} |
||||
|
||||
func NewPromiseAll(timeout time.Duration) *PromiseAll { |
||||
return &PromiseAll{ |
||||
timeout: timeout, |
||||
keys: make(map[string]bool), |
||||
data: make(map[string]any), |
||||
} |
||||
} |
||||
|
||||
func (p *PromiseAll) tick(now time.Time) (finish bool) { |
||||
if p.stime.Add(p.timeout).After(now) { |
||||
p.finish(ErrorTimeout) |
||||
return true |
||||
} |
||||
return p.finallyCall != nil |
||||
} |
||||
|
||||
func (p *PromiseAll) finish(err error) { |
||||
if p.finallyCall == nil { |
||||
return |
||||
} |
||||
// 执行结束回调函数
|
||||
go p.finallyCall(p.data, err) |
||||
// promise 状态结束
|
||||
p.finallyCall = nil |
||||
} |
||||
|
||||
func (p *PromiseAll) Subscribe(keys ...string) *PromiseAll { |
||||
for _, key := range keys { |
||||
p.keys[key] = false |
||||
} |
||||
p.subs = len(p.keys) |
||||
return p |
||||
} |
||||
|
||||
func (p *PromiseAll) Finally(func(data map[string]any, err error)) *PromiseAll { |
||||
if p.subs <= 0 { |
||||
p.finish(nil) |
||||
return p |
||||
} |
||||
|
||||
if p.timeout != NoExpire { |
||||
promiseTickerLock.Lock() |
||||
promiseTicks = append(promiseTicks, p) |
||||
promiseTickerLock.Unlock() |
||||
p.stime = time.Now() |
||||
} |
||||
return p |
||||
} |
||||
|
||||
func (p *PromiseAll) Update(k string, v any, err error) *PromiseAll { |
||||
// options: error stop, concurrent limit
|
||||
p.Lock() |
||||
defer p.Unlock() |
||||
|
||||
finish, ok := p.keys[k] |
||||
if !ok || finish { |
||||
return p |
||||
} |
||||
p.keys[k] = true |
||||
p.subs-- |
||||
if err != nil { |
||||
p.finish(err) |
||||
return p |
||||
} |
||||
|
||||
p.data[k] = v |
||||
if p.subs <= 0 { |
||||
p.finish(nil) |
||||
} |
||||
return p |
||||
} |
||||
Loading…
Reference in new issue