197 lines
3.5 KiB
Go
197 lines
3.5 KiB
Go
package mo
|
|
|
|
import (
|
|
"sync"
|
|
)
|
|
|
|
// NewFuture instanciate a new future.
|
|
func NewFuture[T any](cb func(resolve func(T), reject func(error))) *Future[T] {
|
|
future := Future[T]{
|
|
cb: cb,
|
|
cancelCb: func() {},
|
|
done: make(chan struct{}),
|
|
}
|
|
|
|
future.active()
|
|
|
|
return &future
|
|
}
|
|
|
|
// Future represents a value which may or may not currently be available, but will be
|
|
// available at some point, or an exception if that value could not be made available.
|
|
type Future[T any] struct {
|
|
mu sync.Mutex
|
|
|
|
cb func(func(T), func(error))
|
|
cancelCb func()
|
|
next *Future[T]
|
|
done chan struct{}
|
|
doneOnce sync.Once
|
|
result Result[T]
|
|
}
|
|
|
|
func (f *Future[T]) active() {
|
|
go f.cb(f.resolve, f.reject)
|
|
}
|
|
|
|
func (f *Future[T]) activeSync() {
|
|
f.cb(f.resolve, f.reject)
|
|
}
|
|
|
|
func (f *Future[T]) resolve(value T) {
|
|
f.doneOnce.Do(func() {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
|
|
f.result = Ok(value)
|
|
if f.next != nil {
|
|
f.next.activeSync()
|
|
}
|
|
close(f.done)
|
|
})
|
|
}
|
|
|
|
func (f *Future[T]) reject(err error) {
|
|
f.doneOnce.Do(func() {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
|
|
f.result = Err[T](err)
|
|
if f.next != nil {
|
|
f.next.activeSync()
|
|
}
|
|
close(f.done)
|
|
})
|
|
}
|
|
|
|
// Then is called when Future is resolved. It returns a new Future.
|
|
func (f *Future[T]) Then(cb func(T) (T, error)) *Future[T] {
|
|
f.mu.Lock()
|
|
|
|
next := &Future[T]{
|
|
cb: func(resolve func(T), reject func(error)) {
|
|
if f.result.IsError() {
|
|
reject(f.result.Error())
|
|
return
|
|
}
|
|
newValue, err := cb(f.result.MustGet())
|
|
if err != nil {
|
|
reject(err)
|
|
return
|
|
}
|
|
resolve(newValue)
|
|
},
|
|
cancelCb: func() {
|
|
f.Cancel()
|
|
},
|
|
done: make(chan struct{}),
|
|
}
|
|
f.next = next
|
|
|
|
select {
|
|
case <-f.done:
|
|
f.mu.Unlock()
|
|
next.active()
|
|
default:
|
|
f.mu.Unlock()
|
|
}
|
|
|
|
return next
|
|
}
|
|
|
|
// Catch is called when Future is rejected. It returns a new Future.
|
|
func (f *Future[T]) Catch(cb func(error) (T, error)) *Future[T] {
|
|
f.mu.Lock()
|
|
|
|
next := &Future[T]{
|
|
cb: func(resolve func(T), reject func(error)) {
|
|
if f.result.IsOk() {
|
|
resolve(f.result.MustGet())
|
|
return
|
|
}
|
|
newValue, err := cb(f.result.Error())
|
|
if err != nil {
|
|
reject(err)
|
|
return
|
|
}
|
|
resolve(newValue)
|
|
},
|
|
cancelCb: func() {
|
|
f.Cancel()
|
|
},
|
|
done: make(chan struct{}),
|
|
}
|
|
f.next = next
|
|
|
|
select {
|
|
case <-f.done:
|
|
f.mu.Unlock()
|
|
next.active()
|
|
default:
|
|
f.mu.Unlock()
|
|
}
|
|
|
|
return next
|
|
}
|
|
|
|
// Finally is called when Future is processed either resolved or rejected. It returns a new Future.
|
|
func (f *Future[T]) Finally(cb func(T, error) (T, error)) *Future[T] {
|
|
f.mu.Lock()
|
|
|
|
next := &Future[T]{
|
|
cb: func(resolve func(T), reject func(error)) {
|
|
newValue, err := cb(f.result.Get())
|
|
if err != nil {
|
|
reject(err)
|
|
return
|
|
}
|
|
resolve(newValue)
|
|
},
|
|
cancelCb: func() {
|
|
f.Cancel()
|
|
},
|
|
done: make(chan struct{}),
|
|
}
|
|
f.next = next
|
|
|
|
select {
|
|
case <-f.done:
|
|
f.mu.Unlock()
|
|
next.active()
|
|
default:
|
|
f.mu.Unlock()
|
|
}
|
|
|
|
return next
|
|
}
|
|
|
|
// Cancel cancels the Future chain.
|
|
func (f *Future[T]) Cancel() {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
|
|
f.next = nil
|
|
if f.cancelCb != nil {
|
|
f.cancelCb()
|
|
}
|
|
}
|
|
|
|
// Collect awaits and return result of the Future.
|
|
func (f *Future[T]) Collect() (T, error) {
|
|
<-f.done
|
|
return f.result.Get()
|
|
}
|
|
|
|
// Result wraps Collect and returns a Result.
|
|
func (f *Future[T]) Result() Result[T] {
|
|
return TupleToResult(f.Collect())
|
|
}
|
|
|
|
// Either wraps Collect and returns a Either.
|
|
func (f *Future[T]) Either() Either[error, T] {
|
|
v, err := f.Collect()
|
|
if err != nil {
|
|
return Left[error, T](err)
|
|
}
|
|
return Right[error](v)
|
|
}
|