看源码时出现此情此景,如出至宝,由此记录!
目录
可应用场景
多个函数并发执行各自功能的使用,每一个函数就是开启协程执行的一个单元,每个函数中即可进行如复杂网络请求等可支持并发的需求。例如你的项目中某接口中需要执行三个函数,这三个函数都是向指定库执行插入操作且顺序无关,函数返回error,这三个函数即可并发进行,就可用这个设计。
代码设计
自定义错误接口,错误类型转换、及并发的开启,入参有多少个函数,将开启多少个协程,每个函数出现的错误将由通道统一写入切片并返回。
import (
"errors"
"fmt"
"os"
)
type ErrorInterface interface {
error
Errors() []error
}
type agghelper []error
func (agg agghelper) Error() string {
if len(agg) == 0 {
return ""
}
if len(agg) == 1 {
return agg[0].Error()
}
result := fmt.Sprintf("[%s", agg[0].Error())
for i := 1; i < len(agg); i++ {
result += fmt.Sprintf(", %s", agg[i].Error())
}
result += "]"
return result
}
func (agg agghelper) Errors() []error {
return []error(agg)
}
func NewAggHelper(errlist []error) ErrorInterface {
if len(errlist) == 0 {
return nil
}
var errs []error
for _, e := range errlist {
if e != nil {
errs = append(errs, e)
}
}
if len(errs) == 0 {
return nil
}
return agghelper(errs) //转换类型
}
//如果所有函数均成功完成,则返回nil
func ParallelGoroutines(funcs ...func() error) ErrorInterface {
errChan := make(chan error, len(funcs))
for _, f := range funcs {
go func(f func() error) {
// 若函数需要返回数据,则进行相应改造即可,可直接用interface{}
// 或可自定义结构体到chan中, 结构体中组合包括错误在内的多个类型, 与f的返回类型对应即可
errChan <- f()
}(f)
}
errs := make([]error, 0)
for i := 0; i < cap(errChan); i++ {
if err := <-errChan; err != nil {
errs = append(errs, err)
}
}
// 若函数需要返回包括错误在内的多个值,可进行相应改造
return NewAggHelper(errs)
}
使用
用文件写入操作测试如下:
addContent函数用来进行多函数的并发,writeTo用来实现具体的写文件操作
const (
file1Name = "./error/t1.txt"
file2Name = "./error/t2.txt"
)
func writeTo(fileName, content string) error {
dstFile, err := os.Create(fileName)
// 制造错误,如果有错误,将把错误处理成一个切片
//if fileName == file1Name {
err = errors.New("人造错误") //错误切片:[人造错误, 人造错误]
//}
if err != nil {
fmt.Println("写入报错:", err.Error())
return err
}
defer dstFile.Close()
dstFile.WriteString(content + "\n")
fmt.Println("写入完成:", fileName)
return nil
}
func addContent(content string) error {
// 这里放两个函数各自执行各自的写入操作
return ParallelGoroutines(
// 1
func() error {
return writeTo(file1Name, content)
},
// 2
func() error {
return writeTo(file2Name, content)
},
// Your more func...
)
}
// 使用
func main() {
err := addContent("hello")
fmt.Println("报错:", err)
}
没有产生错误和产生错误的情况结果分别如下:
写入完成: ./error/t2.txt
写入完成: ./error/t1.txt
报错: <nil>
写入报错: 人造错误
写入报错: 人造错误
报错: [人造错误, 人造错误]
如果你的每个函数执行返回结果类型不只是error则只需进行相应改造即可,改造办法也很多,代码中进行了说明。