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