【ReactiveX】基于Golang pmlpml/RxGo程序包的二次开发
基于Golang pmlpml/RxGo程序包的二次開發(fā)【閱讀時(shí)間:約20分鐘】
- 一、ReactiveX & RxGo介紹
- 1.ReactiveX
- 2.RxGo
- 二、系統(tǒng)環(huán)境&項(xiàng)目介紹
- 1.系統(tǒng)環(huán)境
- 2.項(xiàng)目的任務(wù)要求
- 三、具體程序設(shè)計(jì)及Golang代碼實(shí)現(xiàn)
- 1.程序設(shè)計(jì)
- 2.filteringOperator數(shù)據(jù)結(jié)構(gòu)、op函數(shù)與newFilterObservable函數(shù)
- 3. Debounce函數(shù)
- 4. Distinct函數(shù)
- 5. ElementAt函數(shù)
- 6. Filter函數(shù)
- 7. First函數(shù)
- 8. IgnoreElements函數(shù)
- 9. Last函數(shù)
- 10. Sample函數(shù)
- 11. Skip函數(shù)
- 12. SkipLast函數(shù)
- 13. Take函數(shù)
- 14. TakeLast函數(shù)
- 四、程序測試
- 1.封裝并使用程序包
- 2.功能測試
- 3.單元測試
- 五、中文 api 文檔
- 六、完整代碼
- 七、References
一、ReactiveX & RxGo介紹
1.ReactiveX
ReactiveX是Reactive Extensions的縮寫,一般簡寫為Rx,最初是LINQ的一個擴(kuò)展,由微軟的架構(gòu)師Erik Meijer領(lǐng)導(dǎo)的團(tuán)隊(duì)開發(fā),在2012年11月開源,Rx是一個編程模型,目標(biāo)是提供一致的編程接口,幫助開發(fā)者更方便的處理異步數(shù)據(jù)流,Rx庫支持.NET、JavaScript和C++,Rx近幾年越來越流行了,現(xiàn)在已經(jīng)支持幾乎全部的流行編程語言了,Rx的大部分語言庫由ReactiveX這個組織負(fù)責(zé)維護(hù),比較流行的有RxJava/RxJS/Rx.NET,社區(qū)網(wǎng)站是 reactivex.io。中文文檔
2.RxGo
Go 語言的 RxGo 看上去就像 go 入門不久的人寫的,很怪異。 但 RxJava 庫寫的很好。
pmlpml/RxGo 模仿 Java 版寫了 Go 版本新實(shí)現(xiàn),已基本實(shí)現(xiàn)了 Creating Observables 和 Transforming Observables 兩類算子。
二、系統(tǒng)環(huán)境&項(xiàng)目介紹
1.系統(tǒng)環(huán)境
操作系統(tǒng):CentOS7
硬件信息:使用virtual box配置虛擬機(jī)(內(nèi)存3G、磁盤30G)
編程語言:GO 1.15.2
2.項(xiàng)目的任務(wù)要求
閱讀 ReactiveX 文檔。請?jiān)?pmlpml/RxGo 基礎(chǔ)上,
- 修改、改進(jìn)它的實(shí)現(xiàn)
- 或添加一組新的操作,如 filtering
該庫的基本組成:
rxgo.go 給出了基礎(chǔ)類型、抽象定義、框架實(shí)現(xiàn)、Debug工具等
generators.go 給出了 sourceOperater 的通用實(shí)現(xiàn)和具體函數(shù)實(shí)現(xiàn)
transforms.go 給出了 transOperater 的通用實(shí)現(xiàn)和具體函數(shù)實(shí)現(xiàn)
三、具體程序設(shè)計(jì)及Golang代碼實(shí)現(xiàn)
1.程序設(shè)計(jì)
在本次二次開發(fā)中,筆者選擇的是第二種實(shí)現(xiàn),即添加一組filtering操作。
在filtering中,我們可以看到該算子的定義及操作如下:
Filtering Observables: Operators that selectively emit items from a source Observable.
Debounce— only emit an item from an Observable if a particular timespan has passed without it emitting another itemDistinct— suppress duplicate items emitted by an ObservableElementAt— emit only item n emitted by an ObservableFilter— emit only those items from an Observable that pass a predicate testFirst— emit only the first item, or the first item that meets a condition, from an ObservableIgnoreElements— do not emit any items from an Observable but mirror its termination notificationLast— emit only the last item emitted by an ObservableSample— emit the most recent item emitted by an Observable within periodic time intervalsSkip— suppress the first n items emitted by an ObservableSkipLast— suppress the last n items emitted by an ObservableTake— emit only the first n items emitted by an ObservableTakeLast— emit only the last n items emitted by an Observable
2.filteringOperator數(shù)據(jù)結(jié)構(gòu)、op函數(shù)與newFilterObservable函數(shù)
首先直接借鑒pmlpml/RxGo中的transforms.go文件,完善filteringOperator數(shù)據(jù)結(jié)構(gòu)、op函數(shù)與newFilterObservable函數(shù)如下:
// filtering node implementation of streamOperator
type filteringOperator struct {opFunc func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool)
}//op op函數(shù)
func (sop filteringOperator) 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.outflowout := o.outflow//fmt.Println(o.name, "operator in/out chan ", in, out)// Schedulergo func() {end := falsefor x := range in {if end {break}// can not pass a interface as parameter (pointer) to gorountion for it may change its value outside!temp := reflect.ValueOf(x)// send an error to stream if the flip not accept errorerr, ok := x.(error)if ok && !o.flip_accept_error {o.sendToFlow(ctx, err, out)continue}if sop.opFunc(ctx, o, temp, out) {end = true}}if o.flip != nil {buffer := (reflect.ValueOf(o.flip))for i := 0; i < buffer.Len(); i++ {o.sendToFlow(ctx, buffer.Index(i).Interface(), out)}}o.closeFlow(out)}()
}//newFilterObservable 新建一個Filter Observabl
func (parent *Observable) newFilterObservable(name string) (o *Observable) {//new Observableo = newObservable()o.Name = name//chain Observablesparent.next = oo.pred = parento.root = parent.root//set optionso.buf_len = BufferLenreturn o
}
3. Debounce函數(shù)
Debounce函數(shù)用于按時(shí)間防抖動,借鑒transforms.go文件的實(shí)現(xiàn)方式,Observable.operator等于具體的算子操作即可,其具體代碼實(shí)現(xiàn)如下:
var count = 0
var tempTime time.Duration//Debounce 按時(shí)間防抖動
func (parent *Observable) Debounce(timespan time.Duration) (o *Observable) {tempTime = timespano = parent.newFilterObservable("debounce")o.flip_accept_error = trueo.flip_sup_ctx = truecount = 0o.operator = debounceOpreturn o
}var debounceOp = filteringOperator{opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {count++go func() {tempCount := counttime.Sleep(tempTime)select {case <-ctx.Done():returndefault:if tempCount == count {o.sendToFlow(ctx, item.Interface(), out)}}}()return false},
}
4. Distinct函數(shù)
Distinct函數(shù)用于過濾掉重復(fù)出現(xiàn)的元素,借鑒transforms.go文件的實(shí)現(xiàn)方式,Observable.operator等于具體的算子操作即可,其具體代碼實(shí)現(xiàn)如下:
var tempMap map[string]bool//Distinct 過濾掉重復(fù)出現(xiàn)的元素
func (parent *Observable) Distinct() (o *Observable) {o = parent.newFilterObservable("distinct")o.flip_accept_error = trueo.flip_sup_ctx = truetempMap = map[string]bool{}o.operator = distinctOpreturn o
}var distinctOp = filteringOperator{opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {itemStr := fmt.Sprintf("%v", item)_, ok := tempMap[itemStr]if !ok {tempMap[itemStr] = trueo.sendToFlow(ctx, item.Interface(), out)}return false},
}
5. ElementAt函數(shù)
ElementAt函數(shù)用于取第幾個元素,借鑒transforms.go文件的實(shí)現(xiàn)方式,Observable.operator等于具體的算子操作即可,其具體代碼實(shí)現(xiàn)如下:
var tempNum int//ElementAt 取第幾個元素
func (parent *Observable) ElementAt(num int) (o *Observable) {tempNum = numo = parent.newFilterObservable("elementAt")o.flip_accept_error = trueo.flip_sup_ctx = truecount = 0o.operator = elementAtOpreturn o
}var elementAtOp = filteringOperator{opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {if count == tempNum {o.sendToFlow(ctx, item.Interface(), out)return true}count++return false},
}
6. Filter函數(shù)
Filter函數(shù)用于過濾出特定的數(shù)據(jù),此處在transforms.go文件中已定義該操作,直接將其復(fù)制到filtering.go文件中即可,另外此處筆者為了避免函數(shù)沖突,將transforms.go中的filter函數(shù)改為了filter2函數(shù)。Observable.operator等于具體的算子操作即可,其具體代碼實(shí)現(xiàn)如下:
// Filter `func(x anytype) bool` filters items in the original Observable and returns
// a new Observable with the filtered items.
func (parent *Observable) Filter(f interface{}) (o *Observable) {// check validation of ffv := reflect.ValueOf(f)inType := []reflect.Type{typeAny}outType := []reflect.Type{typeBool}b, ctx_sup := checkFuncUpcast(fv, inType, outType, true)if !b {panic(ErrFuncFlip)}o = parent.newTransformObservable("filter")o.flip_accept_error = checkFuncAcceptError(fv)o.flip_sup_ctx = ctx_supo.flip = fv.Interface()o.operator = filterOperaterreturn o
}var filterOperater = transOperater{func(ctx context.Context, o *Observable, x reflect.Value, out chan interface{}) (end bool) {fv := reflect.ValueOf(o.flip)var params = []reflect.Value{x}rs, skip, stop, e := userFuncCall(fv, params)var item interface{} = rs[0].Interface()if stop {end = truereturn}if skip {return}if e != nil {item = e}// send dataif !end {if b, ok := item.(bool); ok && b {end = o.sendToFlow(ctx, x.Interface(), out)}}return
}}
7. First函數(shù)
First函數(shù)用于完成時(shí)返回第一個元素,借鑒transforms.go文件的實(shí)現(xiàn)方式,Observable.operator等于具體的算子操作即可,其具體代碼實(shí)現(xiàn)如下:
//First 完成時(shí)返回第一個元素
func (parent *Observable) First() (o *Observable) {o = parent.newFilterObservable("first")o.flip_accept_error = trueo.flip_sup_ctx = trueo.operator = firstOpreturn o
}var firstOp = filteringOperator{opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {o.sendToFlow(ctx, item.Interface(), out)return true},
}
8. IgnoreElements函數(shù)
函數(shù)用于,借鑒transforms.go文件的實(shí)現(xiàn)方式,Observable.operator等于具體的算子操作即可,其具體代碼實(shí)現(xiàn)如下:
9. Last函數(shù)
Last函數(shù)用于完成時(shí)返回最后一個元素,借鑒transforms.go文件的實(shí)現(xiàn)方式,Observable.operator等于具體的算子操作即可,其具體代碼實(shí)現(xiàn)如下:
//Last 完成時(shí)返回最后一個元素
func (parent *Observable) Last() (o *Observable) {o = parent.newFilterObservable("last")o.flip_accept_error = trueo.flip_sup_ctx = trueo.operator = lastOpreturn o
}var lastOp = filteringOperator{opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {o.flip = append([]interface{}{}, item.Interface())return false},
}
10. Sample函數(shù)
Sample函數(shù)用于定期發(fā)射Observable最近發(fā)射的數(shù)據(jù)項(xiàng),借鑒transforms.go文件的實(shí)現(xiàn)方式,Observable.operator等于具體的算子操作即可,其具體代碼實(shí)現(xiàn)如下:
var tempSample chan interface{}//Sample 定期發(fā)射Observable最近發(fā)射的數(shù)據(jù)項(xiàng)
func (parent *Observable) Sample(sample chan interface{}) (o *Observable) {tempSample = sampleo = parent.newFilterObservable("sample")o.flip_accept_error = trueo.flip_sup_ctx = trueo.operator = sampleOPreturn o
}var sampleOP = filteringOperator{opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {var latest interface{} = nillatest = item.Interface()go func() {tempEnd := truefor tempEnd {select {case <-ctx.Done():tempEnd = truecase <-tempSample:if latest != nil {if o.sendToFlow(ctx, latest, out) {tempEnd = false}latest = nil}}}}()return false},
}
11. Skip函數(shù)
Skip函數(shù)用于跳過前n個數(shù)據(jù),借鑒transforms.go文件的實(shí)現(xiàn)方式,Observable.operator等于具體的算子操作即可,其具體代碼實(shí)現(xiàn)如下:
//Skip 跳過前n個數(shù)據(jù)
func (parent *Observable) Skip(num int) (o *Observable) {tempNum = numo = parent.newFilterObservable("skip")o.flip_accept_error = trueo.flip_sup_ctx = truecount = 0o.operator = skipOpreturn o
}var skipOp = filteringOperator{opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {count++if count > tempNum {o.sendToFlow(ctx, item.Interface(), out)}return false},
}
12. SkipLast函數(shù)
SkipLast函數(shù)用于跳過最后n個數(shù)據(jù),借鑒transforms.go文件的實(shí)現(xiàn)方式,Observable.operator等于具體的算子操作即可,其具體代碼實(shí)現(xiàn)如下:
var tempLasts []interface{}//SkipLast 跳過最后n個數(shù)據(jù)
func (parent *Observable) SkipLast(num int) (o *Observable) {tempNum = numo = parent.newFilterObservable("skipLast")o.flip_accept_error = trueo.flip_sup_ctx = truecount = 0o.operator = skipLastOpreturn o
}var skipLastOp = filteringOperator{opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {//var lasts []interface{}if count == tempNum {o.sendToFlow(ctx, tempLasts[0], out)tempLasts = tempLasts[1:]} else {count++}tempLasts = append(tempLasts, item.Interface())return false},
}
13. Take函數(shù)
Take函數(shù)用于取前n個數(shù)據(jù),借鑒transforms.go文件的實(shí)現(xiàn)方式,Observable.operator等于具體的算子操作即可,其具體代碼實(shí)現(xiàn)如下:
//Take 取前n個數(shù)據(jù)
func (parent *Observable) Take(num int) (o *Observable) {tempNum = numo = parent.newFilterObservable("take")o.flip_accept_error = trueo.flip_sup_ctx = truecount = 0o.operator = takeOpreturn o
}var takeOp = filteringOperator{opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {count++if count > tempNum {return true}o.sendToFlow(ctx, item.Interface(), out)return false},
}
14. TakeLast函數(shù)
TakeLast函數(shù)用于取最后n個數(shù)據(jù),借鑒transforms.go文件的實(shí)現(xiàn)方式,Observable.operator等于具體的算子操作即可,其具體代碼實(shí)現(xiàn)如下:
var tempLasts2 []interface{}//TakeLast 取最后n個數(shù)據(jù)
func (parent *Observable) TakeLast(num int) (o *Observable) {tempNum = numo = parent.newFilterObservable("takeLast")o.flip_accept_error = trueo.flip_sup_ctx = truecount = 0o.operator = takeLastOpreturn o
}var takeLastOp = filteringOperator{opFunc: func(ctx context.Context, o *Observable, item reflect.Value, out chan interface{}) (end bool) {count++if count <= tempNum {tempLasts2 = append(tempLasts2, item.Interface())} else {tempLasts2 = tempLasts2[1:]tempLasts2 = append(tempLasts2, item.Interface())}o.flip = tempLasts2return false},
}
四、程序測試
1.封裝并使用程序包
在項(xiàng)目rxgo的目錄下,執(zhí)行如下指令:
go build
在其他路徑下建立main.go,并調(diào)用rxgo庫即可。
2.功能測試
功能測試主要從用戶角度測試程序包的功能,步驟如下:
創(chuàng)建main.go文件,內(nèi)容如下(數(shù)值可自定義):
package mainimport ("fmt""time""github.com/user/rxgo"
)func main() {fmt.Print("Debounce: ")rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).Debounce(2).Subscribe(func(x int) {fmt.Print(x)fmt.Print(" ")})fmt.Println()fmt.Println()fmt.Print("Distinct: ")rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).Distinct().Subscribe(func(x int) {fmt.Print(x)fmt.Print(" ")})fmt.Println()fmt.Println()fmt.Print("ElementAt 3: ")rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).ElementAt(3).Subscribe(func(x int) {fmt.Print(x)fmt.Print(" ")})fmt.Println()fmt.Println()fmt.Print("First: ")rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).First().Subscribe(func(x int) {fmt.Print(x)fmt.Print(" ")})fmt.Println()fmt.Println()//filterfmt.Print("Filter value < 4: ")res := []int{}rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).Filter(func(x int) bool {return x < 4}).Subscribe(func(x int) {res = append(res, x)})for i := range res {fmt.Print(res[i])fmt.Print(" ")}fmt.Println()fmt.Println()fmt.Print("IgnoreElements: ")rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).IgnoreElements().Subscribe(func(x int) {fmt.Print(x)})fmt.Println()fmt.Println()fmt.Print("Last: ")rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).Last().Subscribe(func(x int) {fmt.Print(x)fmt.Print(" ")})fmt.Println()fmt.Println()fmt.Print("Sample: ")observableP := make(chan interface{})go func() {rxgo.Just(1, 2).Map(func(x int) int {switch x {case 1:time.Sleep(10 * time.Millisecond)case 2:time.Sleep(5 * time.Millisecond)default:time.Sleep(10 * time.Millisecond)}return x}).Subscribe(func(x int) {observableP <- x})}()rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).Map(func(x int) int {time.Sleep(3 * time.Millisecond)return x}).Sample(observableP).Subscribe(func(x int) {fmt.Print(x)fmt.Print(" ")})fmt.Println()fmt.Println()fmt.Print("Skip 2: ")rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).Skip(2).Subscribe(func(x int) {fmt.Print(x)fmt.Print(" ")})fmt.Println()fmt.Println()fmt.Print("SkipLast 2: ")rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).SkipLast(2).Subscribe(func(x int) {fmt.Print(x)fmt.Print(" ")})fmt.Println()fmt.Println()fmt.Print("Take 4: ")rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).Take(4).Subscribe(func(x int) {fmt.Print(x)fmt.Print(" ")})fmt.Println()fmt.Println()fmt.Print("TakeLast 3: ")rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).TakeLast(3).Subscribe(func(x int) {fmt.Print(x)fmt.Print(" ")})fmt.Println()fmt.Println()}
運(yùn)行結(jié)果:
由此可知程序包的功能測試結(jié)果正確,符合的程序包中關(guān)于filtering操作的定義。
3.單元測試
單元測試主要從程序員角度,對程序包的具體函數(shù)進(jìn)行測試。
建立filtering_test.go文件,對程序包的每個函數(shù)進(jìn)行單元測試,此處同樣借鑒pmlpml/RxGo中的transform_test文件,內(nèi)容如下:
package rxgo_testimport ("testing""github.com/stretchr/testify/assert""github.com/user/rxgo"
)func TestDebounce(t *testing.T) {res := []int{}ob := rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).Map(func(x int) int {return x}).Debounce(1)ob.Subscribe(func(x int) {res = append(res, x)})assert.Equal(t, []int{1}, res, "Debounce Test Errorr")
}func TestDistinct(t *testing.T) {res := []int{}ob := rxgo.Just(1, 8, 3, 4, 2, 0, 2, 6).Map(func(x int) int {return x}).Distinct()ob.Subscribe(func(x int) {res = append(res, x)})assert.Equal(t, []int{1, 8, 3, 4, 2, 0, 6}, res, "Distinct Test Errorr")
}func TestElementAt(t *testing.T) {res := []int{}ob := rxgo.Just(1, 8, 3, 4, 2, 0, 6).Map(func(x int) int {return x}).ElementAt(2)ob.Subscribe(func(x int) {res = append(res, x)})assert.Equal(t, []int{3}, res, "SkipLast Test Errorr")
}func TestFirst(t *testing.T) {res := []int{}ob := rxgo.Just(1, 8, 3, 4, 2, 0, 6).Map(func(x int) int {return x}).First()ob.Subscribe(func(x int) {res = append(res, x)})assert.Equal(t, []int{1}, res, "First Test Errorr")
}func TestIgnoreElements(t *testing.T) {res := []int{}ob := rxgo.Just(1, 8, 3, 4, 2, 0, 6).Map(func(x int) int {return x}).IgnoreElements()ob.Subscribe(func(x int) {res = append(res, x)})assert.Equal(t, []int{}, res, "IgnoreElementsTest Errorr")
}func TestLast(t *testing.T) {res := []int{}ob := rxgo.Just(1, 8, 3, 4, 2, 0, 6).Map(func(x int) int {return x}).Last()ob.Subscribe(func(x int) {res = append(res, x)})assert.Equal(t, []int{6}, res, "Last Test Errorr")
}func TestSkip(t *testing.T) {res := []int{}ob := rxgo.Just(1, 8, 3, 4, 2, 0, 6).Map(func(x int) int {return x}).Skip(3)ob.Subscribe(func(x int) {res = append(res, x)})assert.Equal(t, []int{4, 2, 0, 6}, res, "Skip Test Errorr")
}func TestSkipLast(t *testing.T) {res := []int{}ob := rxgo.Just(1, 8, 3, 4, 2, 0, 6).Map(func(x int) int {return x}).SkipLast(3)ob.Subscribe(func(x int) {res = append(res, x)})assert.Equal(t, []int{1, 8, 3, 4}, res, "SkipLast Test Errorr")
}func TestTake(t *testing.T) {res := []int{}ob := rxgo.Just(1, 8, 3, 4, 2, 0, 6).Map(func(x int) int {return x}).Take(4)ob.Subscribe(func(x int) {res = append(res, x)})assert.Equal(t, []int{1, 8, 3, 4}, res, "Take Test Errorr")
}func TestTakeLast(t *testing.T) {res := []int{}ob := rxgo.Just(1, 8, 3, 4, 2, 0, 6).Map(func(x int) int {return x}).TakeLast(4)ob.Subscribe(func(x int) {res = append(res, x)})assert.Equal(t, []int{4, 2, 0, 6}, res, "TakeLast Test Errorr")
}
結(jié)果如下(出于篇幅考慮,部分省略,用…代替):
[henryhzy@localhost rxgo]$ go test -v
=== RUN TestDebounce
--- PASS: TestDebounce (0.00s)
=== RUN TestDistinct
--- PASS: TestDistinct (0.00s)
=== RUN TestElementAt
--- PASS: TestElementAt (0.00s)
=== RUN TestFirst
--- PASS: TestFirst (0.00s)
=== RUN TestIgnoreElements
--- PASS: TestIgnoreElements (0.00s)
=== RUN TestLast
--- PASS: TestLast (0.00s)
=== RUN TestSkip
--- PASS: TestSkip (0.00s)
=== RUN TestSkipLast
--- PASS: TestSkipLast (0.00s)
=== RUN TestTake
--- PASS: TestTake (0.00s)
=== RUN TestTakeLast
--- PASS: TestTakeLast (0.00s)
...
...
ok github.com/user/rxgo 0.005s
[henryhzy@localhost rxgo]$
五、中文 api 文檔
首先安裝godoc如下:
git clone https://github.com/golang/tools $GOPATH/src/golang.org/x/tools
go build golang.org/x/tools
在項(xiàng)目rxgo所在目錄下,執(zhí)行如下指令:
go install
go doc
godoc -url="pkg/github.com/user/rxgo" > API.html
由此可知程序包的單元測試結(jié)果正確,符合的程序包中關(guān)于filtering操作的定義。
六、完整代碼
具體代碼可見gitee倉庫:gitee
七、References
- 課程博客
- ReactiveX官網(wǎng)
總結(jié)
以上是生活随笔為你收集整理的【ReactiveX】基于Golang pmlpml/RxGo程序包的二次开发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【golang程序包推荐分享】分享亿点点
- 下一篇: CentOS Docker安装配置部署G