Go有很好的并发性但承诺了一些时间上的恭维的go例程

58 阅读3分钟

承诺

Go有很好的并发性,但(像javascript一样)承诺了一些时间上的恭维的Go例程。Go例程很好,但它没有等待结果的能力。

虽然有可能使用通道,但有时用承诺的形式来写会更干净(代码上)。

特别是当一大串goroutines运行时,一个简单的promise.Race或promise.All就能满足你的要求。

为什么不直接使用一个通道呢?

promise的好处是,一个承诺已经可以被履行了。在一个已经解决的承诺上运行一个.Await ,就会立即返回已解决的值。而一个通道会再次等待下一个消息(很可能是无限期的一次性响应)。

回调

虽然回调在javascript中被用于承诺,但在go中使用实际的回调行为并没有什么意义,因为在goroutine中阻塞代码是一个完全有效的用例。因此,我们决定简单地使用新承诺的返回值作为返回值

等待/然后

由于回调在go中不是最合理的,所以.Then 是承诺上的一个函数。而在go中,使用.Await ,似乎更加有用

拒绝?错误处理?

虽然错误处理很好,但其建议使用指针来处理模型和返回值。响应中的一个简单的nil ,超过了一些错误,使事情变得非常复杂,不可靠,而且速度更慢。如果数据库在获取模型的时候出现故障,恐慌的感觉是对的。当一个模型没有找到时;返回nil ,感觉很准确。

Lib支持

创建承诺

  • promise.New[T any](cb func() T) *Promise[T]
    • 将启动cb作为goroutine
  • promise.FromChannel[T any](ch <-chan T) *Promise[T]
    • 围绕New的简单包装,一旦通道给出一个结果,就会解决。
  • promise.All[T any](ps ...*Promise[T]) *Promise[[]T]
    • 返回*promise[T[]],接受相同类型的承诺
  • promise.Race[T any](ps ...*Promise[T]) *Promise[T]
    • 返回一个与数组中第一个解决的承诺相匹配的承诺。
  • Resolve[T any](value T) *Promise[T]
    • 返回一个已经解决的承诺

关于承诺的方法

  • p.Await() T
    • 同步函数,返回已解析的承诺的值
  • p.Then(func(v T))
    • 提供一个回调函数,一旦承诺被解析就会被调用
  • p.Chan() chan T
    • 返回一个通道,一旦承诺被解决,它将提供数据。
    • 注意;如果承诺已经被解决,它将成为一个单独使用的通道。

例子

package main

import (
  "fmt"
  "github.com/jaenster/promise"
  "time"
)

type Model struct {
  Id uint64
  X  uint32
  Y  uint32
}

func getModelFromDatabase(id uint64) *promise.Promise[*Model] {
  return promise.New(func() *Model {

    // ... Some code that suppose to get it
    // Blocking code is perfectly valid in go, unlike javascript
    time.Sleep(50 * time.Millisecond)

    // Mock id 2 not being found
    if id == 2 {
      return nil
    }

    // Return instead of resolve
    model := Model{id, 0, 0}
    return &model
  })
}

func main() {
  // Example #1
  // Silly example, as go is fine with blocking code, this could be done without promises just fine
  value := getModelFromDatabase(1).Await()
  fmt.Sprintln(value)

  // Example #2
  // This gets a bit more complex to write with multiple channels and query in native go

  // Start different queries for models
  p := promise.All(getModelFromDatabase(1), getModelFromDatabase(2))

  // Await all results which returns the correctly sorted results as put in at All
  results := p.Await()

  one := results[0]
  two := results[1]

  fmt.Sprintln(one)
  fmt.Sprintln(two)
}