Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing Promise with Channels in Go

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:

  1. Run a goroutine, which return Promise to the main goroutine right away.
  2. Asynchronously do something on the main routine while wait for anything to be sent to either Promise.Result or Promise.Error

  3. Once something is sent, return from the goroutine and make that channel available to be read.

like image 604
Pandemonium Avatar asked Mar 10 '16 20:03

Pandemonium


People also ask

How is channel implemented in Golang?

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.

Do Golang channels block?

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.

Is Go synchronous or asynchronous?

Go ships with it go routines, which executes a function asynchronously. It is a lightweight thread of execution.

Does Golang have async await?

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.


2 Answers

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

like image 199
OneOfOne Avatar answered Sep 22 '22 13:09

OneOfOne


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.

like image 36
beatngu13 Avatar answered Sep 22 '22 13:09

beatngu13