Skip to content

Commit e01a0b5

Browse files
committed
array analogy
1 parent 0bce1e5 commit e01a0b5

File tree

1 file changed

+23
-11
lines changed

1 file changed

+23
-11
lines changed

_chapters/functionalreactiveprogramming.md

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ of(1,2,3,4)
4646
> 3
4747
> 4
4848
49-
![Mouse drag geometry](/assets/images/chapterImages/functionalreactiveprogramming/of1234.gif)
49+
![Using Of](/assets/images/chapterImages/functionalreactiveprogramming/of1234.gif)
5050

5151
The requirement to invoke `subscribe` before anything is produced by the Observable is conceptually similar to the [lazy sequence](lazyevaluation), where nothing happened until we started calling `next`. But there is also a difference.
5252
You could think of our lazy sequences as being “pull-based” data structures, because we had to “pull” the values out one at a time by calling the `next` function as many times as we wanted elements of the list. Observables are a bit different. They are used to handle “streams” of things, such as asynchronous UI (e.g. mouse clicks on an element of a web page) or communication events (e.g. responses from a web service). These things are asynchronous in the sense that we do not know when they will occur.
@@ -71,9 +71,19 @@ range(10)
7171
7272
The three animations represent the creation (`range`) and the two transformations (`filter` and `map`), respectively.
7373

74-
![Mouse drag geometry](/assets/images/chapterImages/functionalreactiveprogramming/even.gif)
74+
![Even Numbers](/assets/images/chapterImages/functionalreactiveprogramming/even.gif)
7575

76-
To solve the first Project Euler problem using RxJS, we generate a sequence of numbers from 0 to 999 with `range(1000)`. We then use the `filter` operator to select numbers divisible by 3 or 5. The `scan` operator, akin to reduce, accumulates the sum of these filtered numbers over time, and the `last` operator emits only the final accumulated sum. Finally, we subscribe to the observable and log the result to the console. Here’s the complete code:
76+
We can relate this to similar operations on arrays which we have seen before:
77+
78+
```javascript
79+
const range = n => Array(n).fill().map((_, i) => i)
80+
range(10)
81+
.filter(isEven)
82+
.map(square)
83+
.forEach(console.log)
84+
```
85+
86+
To solve the first Project Euler problem using RxJS, we generate a sequence of numbers from 0 to 999 with `range(1000)`. We then use the `filter` operator to select numbers divisible by 3 or 5. We then use he `scan` operator, akin to reduce, accumulates the sum of these filtered numbers over time, and the `last` operator emits only the final accumulated sum. Finally, we subscribe to the observable and log the result to the console. Here’s the complete code:
7787

7888
```javascript
7989
range(1000)
@@ -90,7 +100,7 @@ In the developer console, only one number will be printed:
90100
91101
We can see the values changes as they move further and further down the stream. The four animations represent the creation (`range`) and the three transformations (`filter`, `scan` and `last`), respectively. The `last` animation is empty, since we only emit the *last* value, which will be off screen.
92102

93-
![Mouse drag geometry](/assets/images/chapterImages/functionalreactiveprogramming/euler.gif)
103+
![Euler Example](/assets/images/chapterImages/functionalreactiveprogramming/euler.gif)
94104

95105
Scan is very much like the `reduce` function on Array in that it applies an accumulator function to the elements coming through the Observable, except instead of just outputting a single value (as `reduce` does), it emits a stream of the running accumulation (in this case, the sum so far). Thus, we use the `last` function to produce an Observable with just the final value.
96106

@@ -109,7 +119,7 @@ zip(columns,rows)
109119
> ["B",1]
110120
> ["C",2]
111121
112-
![Mouse drag geometry](/assets/images/chapterImages/functionalreactiveprogramming/zip1.gif)
122+
![Zip Example](/assets/images/chapterImages/functionalreactiveprogramming/zip1.gif)
113123

114124
If you like mathy vector speak, you can think of the above as an *inner product* of the two streams.
115125
By contrast, the `mergeMap` operator gives the *cartesian product* of two streams. That is, it gives us a way to take, for every element of a stream, a whole other stream, but flattened (or projected) together with the parent stream. The following enumerates all the row/column indices of cells in a spreadsheet:
@@ -132,7 +142,7 @@ columns.pipe(
132142
> ["C", 1]
133143
> ["C", 2]
134144
135-
![Mouse drag geometry](/assets/images/chapterImages/functionalreactiveprogramming/mergeMap.gif)
145+
![Merge Map Example](/assets/images/chapterImages/functionalreactiveprogramming/mergeMap.gif)
136146

137147
If we contrast `mergeMap` and `map`, map will produce an Observable of Observables, while mergeMap, will produce a single stream with all of the values. Contrast the animation for `map`, with the previous `mergeMap` animation. `map` has three separate branches, where each one represents its own observable stream. The output of the `console.log`, is an instance of the Observable class itself, which is not very useful!
138148

@@ -148,7 +158,7 @@ columns.pipe(
148158
> Observable
149159
> Observable
150160
151-
![Mouse drag geometry](/assets/images/chapterImages/functionalreactiveprogramming/mapmap.gif)
161+
![Map Map Example](/assets/images/chapterImages/functionalreactiveprogramming/mapmap.gif)
152162

153163
Another way to combine streams is `merge`. Streams that are generated with `of` and `range` have all their elements available immediately, so the result of a merge is not very interesting, just the elements of one followed by the elements of the other:
154164

@@ -164,7 +174,7 @@ merge(columns,rows)
164174
> 1
165175
> 2
166176
167-
![Mouse drag geometry](/assets/images/chapterImages/functionalreactiveprogramming/merge.gif)
177+
![Example of Merge](/assets/images/chapterImages/functionalreactiveprogramming/merge.gif)
168178

169179
However, `merge` when applied to asynchronous streams will merge the elements in the order that they arrive in the stream. For example, a stream of key-down and mouse-down events from a web-page:
170180

@@ -176,6 +186,8 @@ const
176186

177187
It’s a convention to end variable names referring to Observable streams with a `$` (I like to think it’s short for “$tream”, or implies a plurality of the things in the stream, or maybe it’s just because [cash rules everything around me](https://www.youtube.com/watch?v=PBwAxmrE194)).
178188

189+
We can analogously think of `mouse$` as an array of `MouseEvent` objects, e.g., `[MouseEvent, MouseEvent, MouseEvent, MouseEvent, MouseEvent]`, and then we can perform operations on this array just as we would with a typical array of values. However, rather than being a fixed array of `MouseEvent`, they are an ongoing stream of `MouseEvent` objects that occur over time. Therefore, instead of being a static collection of events that you can iterate over all at once, the Observable `mouse$` represents a dynamic, potentially infinite sequence of events that are emitted as they happen in real-time.
190+
179191
The following lets us see in the console the keys pressed as they come in, it will keep running for as long as the web page is open:
180192

181193
```javascript
@@ -186,7 +198,7 @@ key$.pipe(
186198

187199
The animation displays the stream as the user types in the best FIT unit in to the webpage
188200

189-
![Mouse drag geometry](/assets/images/chapterImages/functionalreactiveprogramming/keydown.gif)
201+
![Key Down Example](/assets/images/chapterImages/functionalreactiveprogramming/keydown.gif)
190202

191203
The following prints “!!” on every mousedown:
192204

@@ -198,7 +210,7 @@ mouse$.pipe(
198210

199211
The yellow highlight signifies when the mouse is clicked!
200212

201-
![Mouse drag geometry](/assets/images/chapterImages/functionalreactiveprogramming/click.gif)
213+
![Click Example](/assets/images/chapterImages/functionalreactiveprogramming/click.gif)
202214

203215
Once again this will keep producing the message for every mouse click for as long as the page is open. Note that the subscribes do not “block”, so the above two subscriptions will run in parallel. That is, we will receive messages on the console for either key or mouse downs whenever they occur.
204216

@@ -210,7 +222,7 @@ merge(key$.pipe(map(e=>e.key)),
210222
).subscribe(console.log)
211223
```
212224

213-
![Mouse drag geometry](/assets/images/chapterImages/functionalreactiveprogramming/keyboardclick.gif)
225+
![Keyboard Example](/assets/images/chapterImages/functionalreactiveprogramming/keyboardclick.gif)
214226

215227
<div class="cheatsheet" markdown="1">
216228

0 commit comments

Comments
 (0)