承诺
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)
}