go语言中经常使用`sync.WaitGroup`并发执行多个任务,然后使用Wait等待任务执行完成。实际开发中,经常需要在多个任务中调用接口,需要获取到接口返回值和错误,并在外部函数中处理。这样就需要先声明返回值和错误,用起来不太方便。推荐一个简单的三方库`go-future`,支持范型,可以方便的替代WaitGroup,简化并发开发体验:
获取:
```shell
go get github.com/ycl2018/go-future (go >=1.20)
```
##### 使用`sync.WaitGroup`开发对比使用`go-future`
使用`sync.WaitGroup`
```go
func TestWaitGroup2(t *testing.T) {
var swg sync.WaitGroup
swg.Add(2)
// 需要先声明返回值和错误
var ret1, ret2 string
var err1, err2 error
go func() {
defer func() {
// 处理panic
if e := recover(); e != nil {
t.Log(e)
}
}()
defer swg.Done()
ret1 = "bar"
err1 = nil
}()
go func() {
defer func() {
if e := recover(); e != nil {
t.Log(e)
}
}()
defer swg.Done()
ret2 = "bar"
err2 = errors.New("ops")
}()
// 等待任务返回
swg.Wait()
t.Log(ret1, ret2, err1, err2)
}
```
使用`go-future`可以大大简化开发体验
```go
func TestFuture(t *testing.T) {
// 启动异步future任务
f1 := future.Go(func() (string, error) {
return "foo", nil
})
f2 := future.Go(func() (string, error) {
return "bar", nil
})
// 收集结果,自动推算类型无需转换
foo, bar, err := future.Collect(f1, f2)
t.Logf("foo:%s bar:%s err:%v", foo, bar, err)
}
```
##### `go-future`的更多功能:
- [x] 支持范型,根据任务类型返回对应类型的Future,无需类型转换
- [x] 支持多返回值类型任务:从单返回值到至多5个返回值+error
- [x] 支持重复从future中`Wait`获取结果,并发高效、安全
- [x] 支持`Collect`多个Future任务,等待完成并收集结果和错误,8个值内提供范型支持
- [x] 支持`Then`在Future任务完成且无错误返回时执行后置任务
- [x] 支持`Handle`在Future任务完成时检查错误并执行后置处理
- [x] 支持`Check`在链路节点完成时进行错误处理/结果检查
- [x] 支持链式`Join`其他Future任务
- [x] 支持设置超时时间
#### 综合使用demo
```go
// 做番茄炒鸡蛋&番茄牛腩
func TestFuture2(t *testing.T) {
// 洗菜然后切菜
washAndCut := future.Go(func() ([]string, error) {
var ret = []string{"西红柿", "牛腩"}
log.Printf("洗好了%v", ret)
return ret, nil
}).Then(func(washed []string) (any, error) {
log.Printf("切好了%v", washed)
return washed, nil
})
// 打几个鸡蛋
hitEggs := future.Go(func() (string, error) {
log.Printf("打好了鸡蛋")
return "鸡蛋", nil
})
// 做番茄炒蛋
doTomatoEggs := washAndCut.
Join(hitEggs).
Then(func(vegetable any, eggs any) (any, error) {
v := vegetable.([]string)
return fmt.Sprintf("炒好的[%s%s]", v[0], eggs), nil
})
// 做番茄牛腩
doTomatoBeef := washAndCut.Then(func(vegetable any) (any, error) {
v := vegetable.([]string)
return fmt.Sprintf("炒好的[%s%s]", v[0], v[1]), nil
})
// 等待所有任务完成
v0, v1, _ := future.Collect(doTomatoBeef, doTomatoEggs)
t.Logf("最终获得了:%s,%s", v0, v1)
//2024/11/14 14:15:09 打好了鸡蛋
//2024/11/14 14:15:09 洗好了[西红柿 牛腩]
//2024/11/14 14:15:09 切好了[西红柿 牛腩]
// future_test.go:34: 最终获得了:炒好的[西红柿牛腩],炒好的[西红柿鸡蛋]
//--- PASS: TestFuture (0.00s)
//PASS
}
```
### 项目地址:[开源地址](https://github.com/ycl2018/go-future)
**觉得好用记得给作者点个🌟🌟~**
有疑问加站长微信联系(非本文作者)