You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: README.md
+183-39
Original file line number
Diff line number
Diff line change
@@ -12,11 +12,11 @@ Reactive Extensions for the Go Language
12
12
13
13
ReactiveX is a new, alternative way of asynchronous programming to callbacks, promises and deferred. It is about processing streams of events or items, with events being any occurrences or changes within the system. A stream of events is called an [Observable](http://reactivex.io/documentation/contract.html).
14
14
15
-
An operator is basically a function that defines an Observable, how and when it should emit data. The list of operators covered is available [here](README.md#supported-operators-in-rxgo).
15
+
An operator is a function that defines an Observable, how and when it should emit data. The list of operators covered is available [here](README.md#supported-operators-in-rxgo).
16
16
17
17
## RxGo
18
18
19
-
The RxGo implementation is based on the [pipelines](https://blog.golang.org/pipelines) concept. In a nutshell, a pipeline is a series of stages connected by channels, where each stage is a group of goroutines running the same function.
19
+
The RxGo implementation is based on the concept of [pipelines](https://blog.golang.org/pipelines). In a nutshell, a pipeline is a series of stages connected by channels, where each stage is a group of goroutines running the same function.
20
20
21
21

22
22
@@ -48,14 +48,16 @@ go get -u github.com/reactivex/rxgo/v2
48
48
Let's create our first Observable and consume an item:
The `Just` operator creates an Observable from a static list of items. `Of(value)` creates an item from a given value. If we want to create an item from an error, we have to use `Error(err)`. This is a difference with the v1 that was accepting directly a value or an error without having to wrap it. What's the rationale for this change? It is to prepare RxGo for the generics feature coming (hopefully) in Go 2.
58
58
59
+
By the way, the `Just` operator uses currying as a syntactic sugar. This way, it accepts multiple items in the first parameter list and multiple options in the second parameter list. We'll see below how to specify options.
60
+
59
61
Once the Observable is created, we can observe it using `Observe()`. By default, an Observable is lazy in the sense that it emits items only once a subscription is made. `Observe()` returns a `<-chan rxgo.Item`.
60
62
61
63
We consumed an item from this channel and printed its value of the item using `item.V`.
@@ -97,54 +99,71 @@ In this example, we passed 3 functions:
97
99
<-observable.ForEach(...)
98
100
```
99
101
100
-
### Deep Dive
102
+
### Real-World Example
101
103
102
-
Let's implement a more complete example. We will create an Observable from a channel and implement three operators (`Map`, `Filter` and `Reduce`):
104
+
Let's say we want to implement a stream that consumes the following `Customer` structure:
105
+
```go
106
+
typeCustomerstruct {
107
+
IDint
108
+
Name, LastNamestring
109
+
Ageint
110
+
TaxNumberstring
111
+
}
112
+
```
103
113
114
+
We create an producer that will emit `Customer`s to a given `chan rxgo.Item` and create an Observable from it:
We started by defining a `chan rxgo.Item` that will act as an input channel. We also created a `producer` goroutine that will be in charge to produce the inputs for our Observable.
125
+
Then, we need to perform the two following operations:
126
+
* Filter the customers whose age is below 18
127
+
* Enrich each customer with a tax number. Retrieving a tax number is done for example by an IO-bound function doing an external REST call.
135
128
136
-
The Observable was created using `FromChannel` operator. This is basically a wrapper on top of an existing channel. Then, we implemented 3 operators:
137
-
*`Map` to double each input.
138
-
*`Filter` to filter items equals to 4.
139
-
*`Reduce` to perform a reduction based on the product of each item.
129
+
As the enriching step is IO-bound, it might be interesting to parallelize it within a given pool of goroutines.
130
+
Yet, for some reason, all the `Customer` items need to be produced sequentially based on its `ID`.
140
131
141
-
In the end, `Observe` returns the output channel. We consume the output using the standard `<-` operator and we print the value.
142
-
143
-
In this example, the Observable does produce zero or one item. This is what we would expect from a basic `Reduce` operator.
144
-
145
-
An Observable that produces exactly one item is a Single (e.g. `Count`, `FirstOrDefault`, etc.). An Observable that produces zero or one item is called an OptionalSingle (e.g. `Reduce`, `Max`, etc.).
// Serialize the items emitted by their Customer.ID
152
+
rxgo.Serialize(func(item interface{}) int {
153
+
customer:= item.(Customer)
154
+
return customer.ID
155
+
}), rxgo.WithBufferedChannel(1))
156
+
```
146
157
147
-
The operators producing a Single or OptionalSingle emit an item (or no item in the case of an OptionalSingle) when the stream is closed. In this example, the action that triggers the closure of the stream is when the `input` channel will be closed (most likely by the producer).
158
+
In the end, we consume the items using `ForEach()` or `Observe()` for example. `Observe()` returns a `<-chan Item`:
In this example, we create a pool of 32 goroutines that consume items concurrently from the same channel. If the operation is CPU-bound, we can use the `WithCPUPool()` option that creates a pool based on the number of logical CPUs.
265
284
266
-
## Supported Operators in RxGo
285
+
### Connectable Observable
286
+
287
+
A Connectable Observable resembles an ordinary Observable, except that it does not begin emitting items when it is subscribed to, but only when its connect() method is called. In this way, you can wait for all intended Subscribers to subscribe to the Observable before the Observable begins emitting items.
288
+
289
+
Let's create a Connectable Observable using `rxgo.WithPublishStrategy`:
If `observable` was not a Connectable Observable, as `DoOnNext` creates an Observer, the source Observable would have begun emitting items. Yet, in the case of a Connectable Observable, we have to call `Connect()`:
319
+
320
+
```go
321
+
observable.Connect()
322
+
```
323
+
324
+
Once `Connect()` is called, the Connectable Observable begin to emit items.
325
+
326
+
There is another important change with a regular Observable. A Connectable Observable publishes its items. It means, all the Observers receive a copy of the items.
0 commit comments