Let's Go
简介
ReactiveX是Reactive Extensions的缩写,一般简写为Rx,最初是LINQ的一个扩展,由微软的架构师Erik Meijer领导的团队开发,在2012年11月开源,Rx是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步数据流,Rx库支持.NET、JavaScript和C++,Rx近几年越来越流行了,现在已经支持几乎全部的流行编程语言了,Rx的大部分语言库由ReactiveX这个组织负责维护,比较流行的有RxJava/RxJS/Rx.NET,社区网站是 reactivex.io。
为什么要重写
Go 语言的 RxGo 看上去就像 go 入门不久的人写的,很怪异。 但 RxJava 库写的很好。pmlpml/RxGo 模仿 Java 版写了 Go 版本新实现,已基本实现了 Creating Observables 和 Transforming Observables 两类算子。
修改、改进 RxGo 包
阅读 ReactiveX 文档。请在 pmlpml/RxGo 基础上,
- 修改、改进它的实现
- 或添加一组新的操作,如 filtering
- 该库的基本组成:
- rxgo.go 给出了基础类型、抽象定义、框架实现、Debug工具等
- generators.go 给出了 sourceOperater 的通用实现和具体函数实现
- transforms.go 给出了 transOperater 的通用实现和具体函数实现
FilteringOperator
FilteringOperator结构体实现
type filteringOperator struct {
opFunc func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool)
}
Filtering Observables
- Debounce — only emit an item from an Observable if a particular timespan has passed without it emitting another item
- Distinct — suppress duplicate items emitted by an Observable
- ElementAt — emit only item n emitted by an Observable
- Filter — emit only those items from an Observable that pass a predicate test
- Find — emit the first item passing a predicate then complete
- First — emit only the first item or the first item that meets a condition, from an Observable
- IgnoreElements — do not emit any items from an Observable but mirror its termination notification
- Last — emit only the last item emitted by an Observable
- Sample — emit the most recent item emitted by an Observable within periodic time intervals
- Skip — suppress the first n items emitted by an Observable
- SkipLast — suppress the last n items emitted by an Observable
- Take — emit only the first n items emitted by an Observable
- TakeLast — emit only the last n items emitted by an Observable
Debounce
only emit an item from an Observable if a particular timespan has passed without it emitting another item
func (parent *Observable) Debounce(timespan time.Duration) (o *Observable) {
o = parent.newFilterObservable("debounce")
o.flip_accept_error = true
o.flip_sup_ctx = true
count := 0
o.operator = filteringOperator{
opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {
count++
go func() {
tempCount := count
time.Sleep(timespan)
select {
case <-ctx.Done():
return
default:
if tempCount == count {
o.sendToFlow(ctx, item.Interface(), out)
}
}
}()
return false
},
}
return o
}
Distinct
suppress duplicate items emitted by an Observable
func (parent *Observable) Distinct() (o *Observable) {
o = parent.newFilterObservable("distinct")
o.flip_accept_error = true
o.flip_sup_ctx = true
m := map[string]bool{}
o.operator = filteringOperator{
opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {
itemStr := fmt.Sprintf("%v", item)
if _, ok := m[itemStr]; !ok {
m[itemStr] = true
o.sendToFlow(ctx, item.Interface(), out)
}
return false
},
}
return o
}
ElementAt
emit only item n emitted by an Observable
func (parent *Observable) ElementAt(num int) (o *Observable) {
o = parent.newFilterObservable("elementAt.n")
o.flip_accept_error = true
o.flip_sup_ctx = true
count := 0
o.operator = filteringOperator{
opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {
if count == num {
o.sendToFlow(ctx, item.Interface(), out)
return true
}
count++
return false
},
}
return o
}
First
emit only the first item or the first item that meets a condition, from an Observable
func (parent *Observable) First() (o *Observable) {
o = parent.newFilterObservable("first")
o.flip_accept_error = true
o.flip_sup_ctx = true
o.operator = filteringOperator{
opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {
o.sendToFlow(ctx, item.Interface(), out)
return true
},
}
return o
}
IgnoreElements
do not emit any items from an Observable but mirror its termination notification
func (parent *Observable) IgnoreElements() (o *Observable) {
o = parent.newFilterObservable("ignoreElements")
o.flip_accept_error = true
o.flip_sup_ctx = true
o.operator = filteringOperator{
opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {
return false
},
}
return o
}
Last
emit only the last item emitted by an Observable
func (parent *Observable) Last() (o *Observable) {
o = parent.newFilterObservable("last")
o.flip_accept_error = true
o.flip_sup_ctx = true
o.operator = filteringOperator{
opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {
o.flip = append([]interface{}{}, item.Interface())
return false
},
}
return o
}
Sample
emit the most recent item emitted by an Observable within periodic time intervals
func (parent *Observable) Sample(sample chan interface{}) (o *Observable) {
o = parent.newFilterObservable("sample")
o.flip_accept_error = true
o.flip_sup_ctx = true
var latest interface{} = nil
o.operator = filteringOperator{
opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {
latest = item.Interface()
go func() {
tempEnd := true
for tempEnd {
select {
case <-ctx.Done():
tempEnd = true
case <-sample:
if latest != nil {
if o.sendToFlow(ctx, latest, out) {
tempEnd = false
}
latest = nil
}
}
}
}()
return false
},
}
return o
}
Skip
suppress the first n items emitted by an Observable
func (parent *Observable) Skip(num int) (o *Observable) {
o = parent.newFilterObservable("skip.n")
o.flip_accept_error = true
o.flip_sup_ctx = true
count := 0
o.operator = filteringOperator{
opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {
count++
if count > num {
o.sendToFlow(ctx, item.Interface(), out)
}
return false
},
}
return o
}
SkipLast
suppress the last n items emitted by an Observable
func (parent *Observable) SkipLast(num int) (o *Observable) {
o = parent.newFilterObservable("skipLast.n")
o.flip_accept_error = true
o.flip_sup_ctx = true
count := 0
var lasts []interface{}
o.operator = filteringOperator{
opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {
if count == num {
o.sendToFlow(ctx, lasts[0], out)
lasts = lasts[1:]
} else {
count++
}
lasts = append(lasts, item.Interface())
return false
},
}
return o
}
Take
emit only the first n items emitted by an Observable
func (parent *Observable) Take(num int) (o *Observable) {
o = parent.newFilterObservable("take.n")
o.flip_accept_error = true
o.flip_sup_ctx = true
count := 0
o.operator = filteringOperator{
opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {
count++
if count > num {
return true
}
o.sendToFlow(ctx, item.Interface(), out)
return false
},
}
return o
}
TakeLast
emit only the last n items emitted by an Observable
func (parent *Observable) TakeLast(num int) (o *Observable) {
o = parent.newFilterObservable("takeLast.n")
o.flip_accept_error = true
o.flip_sup_ctx = true
count := 0
var lasts []interface{}
o.operator = filteringOperator{
opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {
count++
if count <= num {
lasts = append(lasts, item.Interface())
} else {
lasts = lasts[1:]
lasts = append(lasts, item.Interface())
}
o.flip = lasts
return false
},
}
return o
}
单元测试
完成filtering相关功能后,编写单元测试测试函数功能
func TestDistinct(t *testing.T) {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Distinct().Subscribe(func(x int) {
fmt.Print(x)
})
fmt.Println()
//Output:123456789
}
func TestElementAt(t *testing.T) {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).ElementAt(5).Subscribe(func(x int) {
fmt.Print(x)
})
fmt.Println()
//Output:6
}
func TestFirst(t *testing.T) {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).First().Subscribe(func(x int) {
fmt.Print(x)
})
fmt.Println()
//Output:1
}
func TestIgnoreElements(t *testing.T) {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).IgnoreElements().Subscribe(func(x int) {
fmt.Print(x)
})
fmt.Println()
//Output:
}
func TestLast(t *testing.T) {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Last().Subscribe(func(x int) {
fmt.Print(x)
})
fmt.Println()
//Output:9
}
func TestSample(t *testing.T) {
observableP := make(chan interface{})
go func() {
Just(1, 2, 3, 4, 5).Map(func(x int) int {
switch x {
case 1:
time.Sleep(0 * time.Millisecond)
case 2:
time.Sleep(1 * time.Millisecond)
case 3:
time.Sleep(2 * time.Millisecond)
case 4:
time.Sleep(2 * time.Millisecond)
default:
time.Sleep(2 * time.Millisecond)
}
return x
}).Subscribe(func(x int) {
observableP <- x
})
}()
Just(1, 2, 3, 4, 5).Map(func(x int) int {
time.Sleep(2 * time.Millisecond)
return x
}).Sample(observableP).Subscribe(func(x int) {
fmt.Print(x)
})
fmt.Println()
//Output:123
}
func TestSkip(t *testing.T) {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Skip(2).Subscribe(func(x int) {
fmt.Print(x)
})
fmt.Println()
//Output:3456789
}
func TestSkipLast(t *testing.T) {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).SkipLast(2).Subscribe(func(x int) {
fmt.Print(x)
})
fmt.Println()
//Output:1234567
}
func TestTake(t *testing.T) {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Take(4).Subscribe(func(x int) {
fmt.Print(x)
})
fmt.Println()
//Output:1234
}
func TestTakeLast(t *testing.T) {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).TakeLast(4).Subscribe(func(x int) {
fmt.Print(x)
})
fmt.Println()
//Output:6789
}
func TestDebounce(t *testing.T) {
Just(0, 1, 2, 3, 4, 5).Map(func(x int) int {
switch x {
case 0:
time.Sleep(0 * time.Millisecond)
case 1:
time.Sleep(1 * time.Millisecond)
case 2:
time.Sleep(2 * time.Millisecond)
case 3:
time.Sleep(3 * time.Millisecond)
case 4:
time.Sleep(4 * time.Millisecond)
default:
time.Sleep(1 * time.Millisecond)
}
return x
}).Debounce(2 * time.Millisecond).Subscribe(func(x int) {
fmt.Print(x)
})
fmt.Println()
//Output:12
}
运行单元测试成功
基准测试
func BenchmarkDistinct(b *testing.B) {
for i := 0; i < b.N; i++ {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Distinct().Subscribe(func(x int) {})
}
}
func BenchmarkElementAt(b *testing.B) {
for i := 0; i < b.N; i++ {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).ElementAt(5).Subscribe(func(x int) {})
}
}
func BenchmarkFirst(b *testing.B) {
for i := 0; i < b.N; i++ {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).First().Subscribe(func(x int) {})
}
}
func BenchmarkIgnoreElements(b *testing.B) {
for i := 0; i < b.N; i++ {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).IgnoreElements().Subscribe(func(x int) {})
}
}
func BenchmarkLast(b *testing.B) {
for i := 0; i < b.N; i++ {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Last().Subscribe(func(x int) {})
}
}
func BenchmarkSkip(b *testing.B) {
for i := 0; i < b.N; i++ {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Skip(2).Subscribe(func(x int) {})
}
}
func BenchmarkSkipLast(b *testing.B) {
for i := 0; i < b.N; i++ {
for i := 0; i < b.N; i++ {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).SkipLast(2).Subscribe(func(x int) {})
}
}
}
func BenchmarkTake(b *testing.B) {
for i := 0; i < b.N; i++ {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).Take(4).Subscribe(func(x int) {})
}
}
func BenchmarkTakeLast(b *testing.B) {
for i := 0; i < b.N; i++ {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9).TakeLast(4).Subscribe(func(x int) {})
}
}
/*
*
func BenchmarkSample(b *testing.B) {
for i := 0; i < b.N; i++ {
observableP := make(chan interface{})
go func() {
Just(1, 2, 3, 4, 5).Map(func(x int) int {
switch x {
case 1:
time.Sleep(0 * time.Millisecond)
case 2:
time.Sleep(1 * time.Millisecond)
case 3:
time.Sleep(2 * time.Millisecond)
case 4:
time.Sleep(2 * time.Millisecond)
default:
time.Sleep(2 * time.Millisecond)
}
return x
}).Subscribe(func(x int) {
observableP <- x
})
}()
Just(1, 2, 3, 4, 5).Map(func(x int) int {
time.Sleep(2 * time.Millisecond)
return x
}).Sample(observableP).Subscribe(func(x int) {})
}
}
func BenchmarkDebounce(b *testing.B) {
for i := 0; i < b.N; i++ {
Just(0, 1, 2, 3, 4, 5).Map(func(x int) int {
switch x {
case 0:
time.Sleep(0 * time.Millisecond)
case 1:
time.Sleep(1 * time.Millisecond)
case 2:
time.Sleep(2 * time.Millisecond)
case 3:
time.Sleep(3 * time.Millisecond)
case 4:
time.Sleep(4 * time.Millisecond)
default:
time.Sleep(1 * time.Millisecond)
}
return x
}).Debounce(2 * time.Millisecond).Subscribe(func(x int) {
})
}
}
*/
运行基准测试成功