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: Part 4 - Concurrency/3. Sequences of coincidence.md
+13-13Lines changed: 13 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,10 +1,10 @@
1
1
# Sequences of coincidence
2
2
3
-
Rx tries to avoid state outside of the pipeline. However, some things are inherently stateful. A server can be up or down, a mobile device may have access to wifi, a button is held down. In Rx, we see those as events with a duration. We call them windows. Other events that happen within those windows may need to be treated differently. For example, a mobile device will postpone network requests with low priority while using more expensive channels of communication.
3
+
Rx tries to avoid state outside of the pipeline. However, some things are inherently stateful. A server can be up or down, a mobile device may have access to wifi, a button is held down. In Rx, we see those as events with a duration and we call them windows. Other events that happen within those windows may need to be treated differently. For example, a mobile device will postpone network requests with low priority while using more expensive channels of communication.
4
4
5
5
## Window
6
6
7
-
With [buffer](https://github.com/Froussios/New-Intro-To-Rx/blob/master/Part%203%20-%20Taming%20the%20sequence/5.%20Time-shifted%20sequences.md#buffer) we saw an operator that can take a sequence and group values into chunks, based on a variety of overloads. The `window` operator has a one for one relationship with `buffer`. The main difference is that it doesn't return the groups in buffered chunks. Instead it returns a sequence of sequences, each sequence corresponding to what would have been a buffer. This means that every emitted observable emits its values as soon as they appear in the source observable, rather than producing them all at the end of the window. That relationship between `buffer` and `window` is immediately apparent by a quick look on the marble diagrams of two corresponding overloads:
7
+
With [buffer](https://github.com/Froussios/New-Intro-To-Rx/blob/master/Part%203%20-%20Taming%20the%20sequence/5.%20Time-shifted%20sequences.md#buffer), we saw an operator that can take a sequence and group values into chunks, based on a variety of overloads. The `window` operator has a one-to-one relationship with `buffer`. The main difference is that it doesn't return the groups in buffered chunks. Instead, it returns a sequence of sequences, each sequence corresponding to what would have been a buffer. This means that every emitted observable emits its values as soon as they appear in the source observable, rather than emitting them all at the end of the window. That relationship between `buffer` and `window` is immediately apparent by a quick look on the marble diagrams of two corresponding overloads:
In this example, a ne window begins every 100ms and lasts 250ms. The first window opens at time 0ms and remains open long enough to catch `[0, 1]` (interval emits the first value at time 100ms). Every subsequent window remains open long enough to catch the next 3 values, except for when the values stop.
97
+
In this example, a new window begins every 100ms and lasts 250ms. The first window opens at time 0ms and remains open long enough to catch `[0, 1]` (interval emits the first value at time 100ms). Every subsequent window remains open long enough to catch the next 3 values, except for when the values stop.
98
98
99
99
### Window with signal
100
100
101
101
Lastly, you can define windows using another observable. Every time your signaling observable emits a value, the old window closes and a new one starts.
Alternatively, to have overlapping windows, you can provide a function that uses the values emitted by your signaling observable to contruct another observable that will signal the closing of the window. When the observable corresponding to a window terminates, the window closes.
105
+
Alternatively, to have overlapping windows, you can provide a function that uses the values emitted by your signaling observable to contruct another observable that will signal the closing of the window. When the observable terminates, the corresponding window closes.
This example is the same as the previous example: a new window opens every 100ms and lasts 250ms, with the exception that the first window starts at 100ms rather than 0ms. We see a difference in results, however. The window that begins at time 100ms does not catch the value that is emitted at 100ms, and the same goes for every other window. This happens because the `interval` event that begins the window fires just after the `interval` event that is the value. Even though the two events are simultaneous in theory, in practice there is no such guarantee.
129
+
This example is the same as the previous example: a new window opens every 100ms and lasts 250ms, with the exception that the first window starts at 100ms rather than 0ms. We see a difference in results, however. The window that begins at time 100ms does not catch the value that is emitted at 100ms, and the same goes for every other window. This happens because the `interval` event that begins the window fires just after the `interval` event that is the value. Even though the two events are simultaneous in theory, in practice there is no such thing.
130
130
131
131
## Join
132
132
133
-
`join`allow you to pair items from two sequences together. We've already seen `zip`, which pairs value based on their index. `join` allows you to pair values based on durations. Let's see the signature first:
133
+
`join`allows you to pair together items from two sequences. We've already seen `zip`, which pairs values based on their index. `join` allows you to pair values based on durations. Let's see the signature first:
@@ -140,9 +140,9 @@ public final <TRight,TLeftDuration,TRightDuration,R> Observable<R> join(
140
140
Func2<T,TRight,R> resultSelector)
141
141
```
142
142
143
-
`join` combines two sequences, called "left" and "right". The method is not static and the left sequence is the one that `join` is being called on. In the signature, we can see two methods called `leftDurationSelector` and `rightDurationSelector` which take as an argument an item of the left and right sequence respectively. They return an observable that defines a duration (i.e. a window), just like in the last overload of `window`. These windows are used to select values to be paired together. Values that are paired are passed to the `resultSelector` function which will combine them into a single value, like `zip` does. That value will be emitted by `join`.
143
+
`join` combines two sequences, called "left" and "right". The method is not `stati`c and the left sequence is implied to be the one that `join` is being called on. In the signature, we can see two methods called `leftDurationSelector` and `rightDurationSelector`, which take as an argument an item of the respective sequence. They return an observable that defines a duration (i.e. a window), just like in the last overload of `window`. These windows are used to select values to be paired together. Values that are paired are passed to the `resultSelector` function which will combine them into a single value, like a `resultSelector` in`zip` does. That value will be emitted by `join`.
144
144
145
-
The thing that makes `join` powerful, but also complicated to grasp, is how values are selected to be paired. Every value that arrives in a sequence begins a window for itself. The corresponding duration selector decides when the window for each value will terminate. While the window is open, any value arriving in the opposite sequence will be paired with it. The process is symmetrical, so let's just consider a case where the items of only one sequence have windows.
145
+
The thing that makes `join` powerful, but also complicated to understand, is how values are selected to be paired. Every value that arrives in a sequence begins a window for itself. The corresponding duration selector decides when the window for each value will terminate. While the window is open, any value arriving in the opposite sequence will be paired with it. The process is symmetrical for the left and right sequences, so let's just consider a case where the items of only one sequence have windows.
146
146
147
147
In the first example, the windows in the left sequence never close, while the windows in the right sequence are 0.
148
148
@@ -180,7 +180,7 @@ L2 - R2
180
180
L3 - R2
181
181
```
182
182
183
-
When a window for a left value never ends, what that means is every value emitted by the right sequence for here on will be paired with the left value. Because here the right sequence has half the frequence of the left sequence, between two right values, two more windows have opened on the left. The first right value is paired with the first 2 left values, the second right value is paired with the first 4 left values, the third with 6 and so on.
183
+
When a window for a left value never ends, what that means is that every value from the left sequence will be paired with every value that comes after it from the right sequence. Because here the right sequence has half the frequence of the left sequence, between two right values, two more windows have opened on the left. The first right value is paired with the first 2 left values, the second right value is paired with the first 4 left values, the third with 6 and so on.
184
184
185
185
Lets change the example and see what happens when left and right emit every 100ms and left windows close after 150ms. What happens then is that every left window remains open long enough to catch two right values: one that is emitted at the same time and another after 100ms.
186
186
@@ -217,7 +217,7 @@ L4 - R5
217
217
```
218
218
219
219
Both sequences have windows. Every value of a sequence is paired with:
220
-
* Any older value of the opposite sequence, if the window of the older sequence is still open
220
+
* Any older value of the opposite sequence, if the window of the older value is still open
221
221
* Any newer value of the opposite sequence, if the window for this value is still open
222
222
223
223
@@ -233,7 +233,7 @@ public final <T2,D1,D2,R> Observable<R> groupJoin(
233
233
Func2<? super T,? super Observable<T2>,? extends R> resultSelector)
234
234
```
235
235
236
-
The signature is the same as `join` exept for the `resultSelector`. Now the result selector takes an item from the left sequence and an observable of values from the right sequence. That observable will emit every right value that the left value is paired with. The pairing in `groupJoin` is symmetrical, just like `join`, but the contruction of results isn't. Instead of the way it is now, the argument of the `resultSelect`could just as well have been a single `GroupedObservable` with the left value as a key and the right values being emitted.
236
+
The signature is the same as `join` exept for the `resultSelector`. Now the result selector takes an item from the left sequence and an observable of values from the right sequence. That observable will emit every right value that the left value is paired with. The pairing in `groupJoin` is symmetrical, just like `join`, but the contruction of results isn't. An alternative implementation of this method could have been if the argument of the `resultSelect`was a single `GroupedObservable`, where the left value is the key and the right values are being emitted.
237
237
238
238
Lets revisit our example from `join` where the windows on the left never close.
239
239
@@ -266,7 +266,7 @@ L4: [R2]
266
266
L5: [R2]
267
267
```
268
268
269
-
In the result selector, we a left value and an observable of right values. We used that to print all the values from the right that were paired to each right value. If you go back to the example using `join`, you'll see that the pairs are the same. What is changes is how they are made available to us to process.
269
+
In the result selector, we have a left value and an observable of right values. We used that to print all the values from the right that were paired to each left value. If you go back to the example which used `join`, you'll see that the pairs are the same. What is changes is how they are made available to us in the `resultSelector`.
270
270
271
271
You can implement `join` with `groupJoin` and `flatMap`
272
272
```java
@@ -286,7 +286,7 @@ You can implement `join` with `groupJoin` and `flatMap`
286
286
.flatMap(i -> i)
287
287
```
288
288
289
-
You can also implement `groupJoin` with `join` and `groupBy`. Doing so would require you to contruct tuples as a result and do `groupBy` on the left part of the tuple. Since Java doesn't provide default tuples, we will leave an example to the reader's imagination.
289
+
You can also implement `groupJoin` with `join` and `groupBy`. Doing so would require you to contruct tuples as a result and do `groupBy` on the left part of the tuple. We will leave the code for this example to the reader's appetite for hands-on.
0 commit comments