I'm trying to implement Promise in Go which would be similar to that in Javascript.
type Promise struct {
Result chan string
Error chan error
}
func NewPromise() (*Promise) {
r := make(chan string, 1)
e := make(chan error, 1)
return &Promise{
Result: r,
Error: e,
}
}
func main() {
var p = NewPromise()
go func(p *Promise) {
time.Sleep(time.Duration(5)*time.Second)
p.Result <- "done"
}(p)
if <- p.Result {
fmt.Println(<-p.Result)
}
// Is it possible to do something else here while wait for 5s?
// Once Promise is fulfilled after 5s, the Result is available.
}
How do I do the following:
Promise
to the main goroutine right
away. Asynchronously do something on the main routine while wait for
anything to be sent to either Promise.Result
or Promise.Error
Once something is sent, return from the goroutine and make that channel available to be read.
In Go, when a goroutine tries to send data to a full channel, it is added to a queue in the channel struct. This causes the goroutine to block. As space becomes available on the buffer, the Go runtime will pop the first goroutine off the queue and add the data to the buffer.
Channel operation (i.e. write or read) are blocking in nature. This means: When we send data into the channel using a GoRoutine, it will be blocked until the data is consumed by another GoRoutine. When we receive data from channel using a GoRoutine, it will be blocked until the data is available in the channel.
Go ships with it go routines, which executes a function asynchronously. It is a lightweight thread of execution.
We have gone through what async/await it and implemented a simple version of that in Golang. I would encourage you to look into async/await a lot more and see how it can ease the readability of the codebase much better.
A different approach without using channels, which makes it a little bit faster / more efficient:
type Promise struct {
wg sync.WaitGroup
res string
err error
}
func NewPromise(f func() (string, error)) *Promise {
p := &Promise{}
p.wg.Add(1)
go func() {
p.res, p.err = f()
p.wg.Done()
}()
return p
}
func (p *Promise) Then(r func(string), e func(error)) {
go func() {
p.wg.Wait()
if p.err != nil {
e(p.err)
return
}
r(p.res)
}()
}
playground
There's a paper called "From Events to Futures and Promises and back" by Martin Sulzmann (published in February 2016), which covers that topic. The abstract says:
Events based on channel communications and futures/promises are powerful but seemingly different concepts for concurrent programming. We show that one concept can be expressed in terms of the other with surprisingly little effort. Our results offer light-weight library based approaches to implement events and futures/promises. Empirical results show that our approach works well in practice.
According to the paper, futures look like this:
type Comp struct {
value interface{}
ok bool
}
type Future chan Comp
func future(f func() (interface{}, bool)) Future {
future := make(chan Comp)
go func() {
v, o := f()
c := Comp{v, o}
for {
future <- c
}
}()
return future
}
Whereas promises are implemented as follows:
type Promise struct {
lock chan int
ft Future
full bool
}
func promise() Promise {
return Promise{make(chan int, 1), make(chan Comp), false}
}
func (pr Promise) future() Future {
return pr.ft
}
Read up the paper for details, combinators and more.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With