ReactiveX,简称为 Rx,是一个异步编程的 API。与 callback(回调)、promise(JS 提供这种方式)和 deferred(Python 的 twisted 网络编程库就是使用这种方式)这些异步编程方式有所不同,Rx 是基于事件流的。这里的事件可以是系统中产生或变化的任何东西,在代码中我们一般用对象表示。在 Rx 中,事件流被称为 Observable(可观察的)。事件流需要被 Observer(观察者)处理才有意义。想象一下,我们日常作为一个 Observer,一个重要的工作就是观察 BUG 的事件流。每次发现一个 BUG,我们都需要去解决它。
Rx 仅仅只是一个 API 规范的定义。Rx 有多种编程语言实现,RxJava/RxJS/Rx.NET/RxClojure/RxSwift
。RxGo 是 Rx 的 Go 语言实现。借助于 Go 语言简洁的语法和强大的并发支持(goroutine、channel),Rx 与 Go 语言的结合非常完美。
pipelines (官方博客:https://blog.golang.org/pipelines)是 Go 基础的并发编程模型。其中包含,fan-in——多个 goroutine 产生数据,一个goroutine 处理数据,fan-out——一个 goroutine 产生数据,多个 goroutine 处理数据,fan-inout——多个 goroutine 产生数据,多个 goroutine 处理数据。它们都是通过 channel 连接。RxGo 的实现就是基于 pipelines 的理念,并且提供了方便易用的包装和强大的扩展。
本文代码使用 Go Modules。
创建目录并初始化:
$ mkdir rxgo && cd rxgo
$ go mod init github.com/darjun/go-daily-lib/rxgo
安装rxgo
库:
$ go get -u github.com/reactivex/rxgo/v2
编码:
package main
import (
"fmt"
"github.com/reactivex/rxgo/v2"
)
func main() {
observable := rxgo.Just(1, 2, 3, 4, 5)()
ch := observable.Observe()
for item := range ch {
fmt.Println(item.V)
}
}
使用 RxGo 的一般流程如下:
Observe()
方法,该方法返回一个<- chan rxgo.Item
。然后for range
遍历即可。GitHub 上一张图很形象地描绘了这个过程:
Just
创建一个仅有若干固定数据的 Observable;Map()
方法执行转换(将圆形转为方形);Filter()
方法执行过滤(过滤掉黄色的方形)。看懂了这张图片,就能了解 RxGo 工作的基本流程了。
上面是简单的示例,没有过滤、转换操作的使用。
运行:
$ go run main.go
1
2
3
4
5
关于上面的示例,需要注意:
Just
使用柯里化(currying)让它可以在第一个参数中接受多个数据,在第二个参数中接受多个选项定制行为。柯里化是函数化编程的思想,简单来说就是通过在函数中返回函数,以此来减少每个函数的参数个数。例如:
func add(value int) func (int) int {
return func (a int) int {
return value + a
}
}
fmt.Prinlnt(add(5)(10)) // 15
由于 Go 不支持多个可变参数,Just
通过柯里化迂回地实现了这个功能:
// rxgo/factory.go
func Just(items ...interface{}) func(opts ...Option) Observable {
return func(opts ...Option) Observable {
return &ObservableImpl{
iterable: newJustIterable(items...)(opts...),
}
}
}
实际上rxgo.Item
还可以包含错误。所以在使用时,我们应该做一层判断:
func main() {
observable := rxgo.Just(1, 2, errors.New("unknown"), 3, 4, 5)()
ch := observable.Observe()
for item := range ch {
if item.Error() {
fmt.Println("error:", item.E)
} else {
fmt.Println(item.V)
}
}
}
运行:
$ go run main.go
1
2
error: unknown
3
4
5
我们使用item.Error()
检查是否出现错误。然后使用item.V
访问数据,item.E
访问错误。
除了使用for range
之外,我们还可以调用 Observable 的ForEach()
方法来实现遍历。ForEach()
接受 3 个回调函数:
NextFunc
:类型为func (v interface {})
,处理数据;ErrFunc
:类型为func (err error)
,处理错误;CompletedFunc
:类型为func ()
,Observable 完成时调用。有点Promise
那味了。使用ForEach()
,可以将上面的示例改写为:
func main() {
observable := rxgo.Just(1, 2, 3, 4, 5)()
<-observable.ForEach(func(v interface{}) {
fmt.Println("received:", v)
}, func(err error) {
fmt.Println("error:", err)
}, func() {
fmt.Println("completed")
})
}
运行:
$ go run main.go
received: 1
received: 2
received: 3
received: 4
received: 5
completed
ForE