You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
2.1 KiB
117 lines
2.1 KiB
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 |
|
}
|
|
|