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
1 2 3 4 5 6 7 8 9 10 11 12 13 | package main import ( "fmt" RxGo "github.com/pmlpml/rxgo" ) func main() {<!-- --> RxGo.Just("Hello", "World", "!").Subscribe(func(x string) {<!-- --> fmt.Println(x) }) } |
hello world结果如图
链式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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打印出
连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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直接搬过来,改下名字跟类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 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”
再写这十二个功能的测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | 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做了简单修改加了这个
1
2
3
4
5
6
7
8
9if 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项
项目代码