ReactiveX
Reactive Extensions的缩写,一般简写为Rx,最初是LINQ的一个扩展,由微软的架构师Erik Meijer领导的团队开发,在2012年11月开源,Rx是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步数据流,Rx库支持.NET、JavaScript和C++,Rx近几年越来越流行了,现在已经支持几乎全部的流行编程语言了,Rx的大部分语言库由ReactiveX这个组织负责维护,比较流行的有RxJava/RxJS/Rx.NET
尝试使用pmlpml/RxGo
github链接
照着readme尝试进行使用
hello world
package main
import (
"fmt"
RxGo "github.com/pmlpml/rxgo"
)
func main() {
RxGo.Just("Hello", "World", "!").Subscribe(func(x string) {
fmt.Println(x)
})
}
hello world结果如图
链式
package main
import (
"fmt"
RxGo "github.com/pmlpml/rxgo"
)
func fibonacci(max int) func() (int, bool) {
a, b := 0, 1
return func() (r int, end bool) {
r = a
a, b = b, a+b
if r > max {
end = true
}
return
}
}
func main() {
RxGo.Start(fibonacci(10)).Map(func(x int) int {
return 2*x
}).Subscribe(func(x int) {
fmt.Print(x)
})
}
链式测试,此次测试先用start定了个0112358的源observable,随后用Map对对象内的数全进行了修改,最后用Subscribe打印出
连接
package main
import (
"fmt"
RxGo "github.com/pmlpml/rxgo"
)
func main() {
//define pipeline
source := RxGo.Just("Hello", "World", "!")
next := source.Map(func(s string) string {
return s + " "
})
//run pipeline
next.Subscribe(func(x string) {
fmt.Print(x)
})
fmt.Println()
source.Subscribe(func(x string) {
fmt.Print(x)
})
}
这个主要测试是否是深修改,结果如下
实验部份,实现Filtering
实验过程
首先要知道实现方法,参照老师已经实现了的filter,观察他的调用函数顺序
Start()只是创建了个observable,并赋予了它的基础属性,直到Subscribe()上才是最终启动
Subscribe()中又动用到了connect(), connect()又动用到了op,这个op接口才是自己需要实现的
新开了个filterings.go之后就开始实现以下函数,
首先把op跟transOperator直接搬过来,改下名字跟类型
type filteringsOperater struct {
opFunc func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool)
}
func (tsop filteringsOperater) op(ctx context.Context, o *Observable) {
// must hold defintion of flow resourcs here, such as chan etc., that is allocated when connected
// this resurces may be changed when operation routine is running.
in := o.pred.outflow
out := o.outflow
//fmt.Println(o.name, "operator in/out chan ", in, out)
var wg sync.WaitGroup
go func() {
end := false
for x := range in {
if end {
continue
}
// can not pass a interface as parameter (pointer) to gorountion for it may change its value outside!
xv := reflect.ValueOf(x)
// send an error to stream if the flip not accept error
if e, ok := x.(error); ok && !o.flip_accept_error {
o.sendToFlow(ctx, e, out)
continue
}
// scheduler
switch threading := o.threading; threading {
case ThreadingDefault:
if tsop.opFunc(ctx, o, xv, out) {
end = true
}
case ThreadingIO:
fallthrough
case ThreadingComputing:
wg.Add(1)
go func() {
defer wg.Done()
if tsop.opFunc(ctx, o, xv, out) {
end = true
}
}()
default:
}
}
wg.Wait() //waiting all go-routines completed
o.closeFlow(out)
}()
}
之后开始尝试实现下面的功能,出现问题了在对上面做改动
先装上 “github.com/stretchr/testify/assert”,“github.com/davecgh/go-spew/spew”,“github.com/pmezard/go-difflib/difflib”
再写这十二个功能的测试
package rxgo_test
import (
"testing"
"time"
"github.com/pmlpml/rxgo"
"github.com/stretchr/testify/assert"
)
func TestDebounce(t *testing.T) {
res := []int{}
ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
if x == 2 || x == 5 {
time.Sleep(3 * time.Millisecond)
} else {
time.Sleep(1 * time.Millisecond)
}
return x
}).Debounce(2 * time.Millisecond)
ob.Subscribe(func(x int) {
res = append(res, x)
})
assert.Equal(t, []int{1, 4, 9}, res, "debounce error")
}
func TestDistinct(t *testing.T) {
res := []int{}
ob := rxgo.Just(1, 2, 3, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
time.Sleep(1 * time.Millisecond)
return x
}).Distinct()
ob.Subscribe(func(x int) {
res = append(res, x)
})
assert.Equal(t, []int{1, 2, 3, 4, 5, 6, 7, 8, 9}, res, "distinct error")
}
func TestElementAt(t *testing.T) {
res := []int{}
ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
time.Sleep(1 * time.Millisecond)
return x
}).ElementAt(1)
ob.Subscribe(func(x int) {
res = append(res, x)
})
assert.Equal(t, []int{2}, res, "elementAt error")
}
func TestFirst(t *testing.T) {
res := []int{}
ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
time.Sleep(1 * time.Millisecond)
return x
}).First()
ob.Subscribe(func(x int) {
res = append(res, x)
})
assert.Equal(t, []int{1}, res, "first error")
}
func TestIgnoreElements(t *testing.T) {
res := []int{}
ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).IgnoreElements()
ob.Subscribe(func(x int) {
res = append(res, x)
})
assert.Equal(t, []int{}, res, "ignore element error")
}
func TestLast(t *testing.T) {
res := []int{}
ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Last()
ob.Subscribe(func(x int) {
res = append(res, x)
})
assert.Equal(t, []int{9}, res, "last error")
}
func TestSkip(t *testing.T) {
res := []int{}
ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
time.Sleep(1 * time.Millisecond)
return x
}).Skip(3)
ob.Subscribe(func(x int) {
res = append(res, x)
})
assert.Equal(t, []int{4, 5, 6, 7, 8, 9}, res, "skip error")
}
func TestSkiplast(t *testing.T) {
res := []int{}
ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
time.Sleep(1 * time.Millisecond)
return x
}).Skiplast(6)
ob.Subscribe(func(x int) {
res = append(res, x)
})
assert.Equal(t, []int{1, 2, 3}, res, "skiplast error")
}
func TestTake(t *testing.T) {
res := []int{}
ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
time.Sleep(1 * time.Millisecond)
return x
}).Take(3)
ob.Subscribe(func(x int) {
res = append(res, x)
})
assert.Equal(t, []int{1, 2, 3}, res, "take error")
}
func TestTakelast(t *testing.T) {
res := []int{}
ob := rxgo.Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Map(func(x int) int {
time.Sleep(1 * time.Millisecond)
return x
}).Takelast(3)
ob.Subscribe(func(x int) {
res = append(res, x)
})
assert.Equal(t, []int{7, 8, 9}, res, "takelast error")
}
写完测试后就是单元实现,实现就不放具体代码了,简要说明一下每个函数在干啥就是了
-
Debounce
Debounce的参数代表从后往前数间隔时间需大于参数才会被筛选出来,主要作用是过滤发射数据过快的项
单元测试结果
-
Distinct
去掉重复的项
这里着重讲一下,本来在测试的时后是没有加map(给每个项延迟避免并发),可能是我实现的时后没有加互斥锁,导致每次测试顺序都不对,避免麻烦就在测试的时后加了延迟。(码力不足见量) -
ElementAt
返回在该下标的项
-
Filter
filter老师已经实现了就不重复做了 -
First
返回第一项
-
IgnoreElements
啥都不返回
-
Last
返回最后一个,由于不知道输入流什么时候结束,所以每输入进一个就存一个,直到没东西为止才输出。
由于使用了filter做判断,所以对op做了简单修改加了这个
if o.flip != nil { buffer, ok := reflect.ValueOf(o.flip).Interface().([]reflect.Value) if !ok { panic("filter.go(op): buffer is not a slice") } for _, v := range buffer{ o.sendToFlow(ctx, v.Interface(), out) } }
-
Skip
跳过前n项
-
SkipLast
跳过后n项,由于不知道尽头在哪,所以弄了个n大小的队列,即可简单实现
-
Take
拿前n项
-
TakeLast
拿后n项