Skip to content

Instantly share code, notes, and snippets.

@shaomingquan
Last active August 20, 2018 12:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shaomingquan/0f9573658ddf72ec8ed606e0dfca3c9e to your computer and use it in GitHub Desktop.
Save shaomingquan/0f9573658ddf72ec8ed606e0dfca3c9e to your computer and use it in GitHub Desktop.
package gopromise
import (
"context"
"errors"
"sync"
"time"
)
//Promise struct
type Promise struct {
lock sync.Mutex
timeout time.Duration
errCheck bool
}
//PromiseCallItem Promise callItem
type PromiseCallItem func() (interface{}, error)
//NewPromise create new Promise instance
func NewPromise(timeout int, errCheck bool) *Promise {
p := &Promise{}
p.lock = sync.Mutex{}
p.timeout = time.Second * time.Duration(timeout)
p.errCheck = errCheck
return p
}
type sortedHelper struct {
result *CallRet
index int
userError bool
}
type CallRet struct {
Val interface{}
Error error
}
func doCall(index int, call PromiseCallItem) <-chan *sortedHelper {
ch := make(chan *sortedHelper)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
wg.Wait()
close(ch)
}()
go func() {
result, err := call()
helper := &sortedHelper{
&CallRet{
result,
err,
},
index,
false,
}
if err != nil {
helper.userError = true
}
ch <- helper
wg.Done()
}()
return ch
}
// All return all callitem
func (p *Promise) All(calls []PromiseCallItem) ([]*CallRet, error) {
ctx, cancel := context.WithTimeout(context.Background(), p.timeout)
ch := make(chan *sortedHelper, len(calls))
defer func() {
cancel()
go func() { // as cancel is async, close is sync
close(ch)
}()
}()
l := len(calls)
for index, call := range calls {
go func(index int, call PromiseCallItem) {
select {
case result := <-doCall(index, call):
ch <- result
case <-ctx.Done():
ch <- &sortedHelper{
&CallRet{
nil,
errors.New("promise call item timeout"),
},
index,
false,
}
}
}(index, call)
}
ret := make([]*CallRet, l)
counter := 0
var err error
for resultItem := range ch {
write := func() {
p.lock.Lock()
ret[resultItem.index] = resultItem.result
p.lock.Unlock()
}
if resultItem.userError && p.errCheck {
ret = []*CallRet{}
err = errors.New("one of your func call failed")
break
} else {
write()
}
counter++
if counter == l {
break
}
}
return ret, err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment