【问题标题】:Implementing Promise with Channels in Go在 Go 中使用通道实现 Promise
【发布时间】:2016-06-25 20:51:30
【问题描述】:

我正在尝试在 Go 中实现 Promise,这与 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.
}

如何执行以下操作:

  1. 运行一个goroutine,它将Promise返回到主goroutine右边 离开。
  2. 在等待时在主例程上异步执行某些操作 任何要发送到Promise.Result 或Promise.Error

  3. 一旦发送了一些东西,从 goroutine 返回并进行 频道可供阅读。

【问题讨论】:

    标签: asynchronous go promise channel


    【解决方案1】:

    一种不使用渠道的不同方法,这使其更快/更高效:

    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

    【讨论】:

      【解决方案2】:

      Martin Sulzmann 有一篇名为 "From Events to Futures and Promises and back" 的论文(发表于 2016 年 2 月),其中涵盖了该主题。摘要说:

      基于通道通信和期货/承诺的事件是并发编程的强大但看似不同的概念。我们表明,一个概念可以用另一个概念来表达,而且几乎不需要付出多少努力。我们的结果提供了基于轻量级库的方法来实现事件和期货/承诺。实证结果表明,我们的方法在实践中效果很好。

      根据论文,期货是这样的:

      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
      }
      

      而 promise 的实现方式如下:

      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
      }
      

      阅读论文以了解详细信息、组合器等。

      【讨论】:

        【解决方案3】:

        我也在尝试实现 javascript 的承诺 :)。这是一个学习目的的项目。在这个实现中,我学习了go的channel、select、goroutine。我认为这个小型图书馆满足您的需求。

        p := New(func(resolve func(interface{}), reject func(error)) {
            resolve("sonla")
        })
        
        p.Then(func(data interface{}) interface{} {
            fmt.Printf("What I get is %v\n", data.(string))
            return nil
        })
        
        Await(p)
        

        欢迎贡献,如果有人有更好的想法在 golang 中实现 promise。这是我的repo

        【讨论】:

          【解决方案4】:

          有很多方法可以做到这一点,但我所做的例如调整 NewPromise() 以将函数作为 arg 接受结果和错误通道。然后 NewPromise 方法用这个函数初始化一个 go 例程,返回带有要读取的相同通道的 Promise。如果你调用 .Then 方法,这基本上需要两个函数作为参数。一种处理您通过结果通道(字符串)传递的类型,另一种处理错误通道的结果类型(错误)。 .Then 方法然后调用 goroutine 中的私有 .then() 方法来选择首先发生的结果或错误,然后调用适合每个结果的函数。

          例如,我只使用了一个简单的ticker,它等待一秒钟,然后通过结果通道发送“hi”。

          我希望这能让您了解一种方法。

          GoLang 游乐场: https://play.golang.org/p/xc1xvv7hRx

          【讨论】:

            【解决方案5】:

            仅使用一个数据通道来实现“承诺”怎么样:

            type Promise[A any] interface {
                Then(handler chan <- A) 
            }
            
            type promiseImpl [A any] struct {   
                value A
                done <- chan struct{}
            }
            
            func (p promiseImpl[A]) Then(handler chan <- A) {
                go func() {
                    <-p.done
                    handler <- p.value
                }()
            }
            
            func MakePromise[A any](resolver <- chan A) Promise[A] {
                done := make(chan struct{})
                result := promiseImpl[A]{done: done}
                go func() {
                    defer close(done)
                    result.value = <- resolver
                }()
                return result
            }
            

            基本的实现思路是利用从封闭通道读取永远不会阻塞的事实。

            所以对于每个Then 调用,我们都会启动一个goroutine。如果 promise 已经解析,它会从关闭的通道中读取,并立即将解析的值分派到处理程序通道。否则它将等到承诺得到解决。

            解析过程是另一个 goroutine,它等待解析的值在源通道上可用,然后记住这些值并关闭触发通道,这将导致所有等待的 goroutine 运行。

            请注意,不需要其他同步原语。

            【讨论】:

              猜你喜欢
              • 2013-11-06
              • 2011-05-26
              • 2018-05-29
              • 1970-01-01
              • 2015-05-20
              • 1970-01-01
              • 2013-01-28
              • 2016-08-09
              • 1970-01-01
              相关资源
              最近更新 更多