Advanced Asynchronous Programming in JavaScript by Jafar Husain.md 4.4 KB
Newer Older
1
# Advanced Asynchronous Programming in JavaScript by Jafar Husain
Derek Knox's avatar
Derek Knox committed
2

3
## Observable
4
The goal of learning about Observable and specifically the `map()`, `filter()`, `reduce()`, `merge()`, and `zip()` operators is so you can:
5
1. code without loops
6
7
    - declarative (manage less state)
    - functional (mutate less state)
8
2. abstract the various async APIs into one API (`next()`, `error()`, `complete()`)
9
10
11
3. employ automatic cleanup
    - mitigate memory leaks (`take()`/`takeUntil()`)
    - error forwarding
12
13
14
15
16

- Observable quotes:
  - "Everything is a stream" - Rob Wormald (ngrx)
  - "Asynchronous arrays" - Kyle Simpson (Frontend Masters - Deep JS Foundations)
  - "An Observable is like a DOM event that can accept three handlers instead of one" - Jafar Husain (Frontend Masters - Asynchronous Programming in JavaScript)
17
- Observable - collection that arrives over time (Observable === Collection + Time)
18
  - cold - "no matter when you subscribe, you get the same data"
19
  - hot - "no matter when you subscribe, you might get different data"
Derek Knox's avatar
Derek Knox committed
20
- Observable leverages the Iterator and Observer pattern to create a best-of-both worlds object:
Derek Knox's avatar
Derek Knox committed
21
22
23
  - Iterator's *push* (producer)
  - Observer's *pull* (consumer)
  - An Observable is capable of denoting update, error, and completion where the typical Observer pattern doesn't have a built-in way for consuming an error or completion. This is the best-of-both worlds aspect that an Observable solves.
24
- Observables can model events, async server requests, animations, and data/arrays in a simplified and uniform way, this is the win. They enable a unified programming model for transforming any collection. Observables push data while providing a mechanism for an observer to pull and consume the changes:
Derek Knox's avatar
Derek Knox committed
25
  - push
26
27
28
    - `next(payload)` - observer's update callback
    - `error(error)` - observer's error callback
    - `complete()` - observer's complete callback
Derek Knox's avatar
Derek Knox committed
29
  - pull
Derek Knox's avatar
Derek Knox committed
30
    - `forEach(observer)` - observer's subscribtion/unsubscription hook for update, error, and completion changes
31
      - `observer = { next: onUpdate, error: onError, complete: onComplete }`
32
      
33
## Observable of Observables Flattening Strategies
34

35
36
37
38
Main three flattening strategies:
1. `concatAll` - observable of observables that mitigates race conditions by ensuring async sequence is respected (similar to flattening a 2D array)
2. `mergeAll` - observable of observables like `concatAll` but sequence is ignored in favor of first-come, first-served
3. `switchLatest` - observable of observables like `concatAll` where dispatches of observables dispose those prior (replaces state machines)
39
    - "Instead of building a machine with a bunch of moving parts to compute an answer, we're going to write the answer... were' going to do this declaratively" - Jafar Husain.
40
41
  
Thought process:
42
- n-dimensional nesting process (same process for Arrays)
43
  1. `map()` until you have an identifier bound to every value you need
44
  2. flatten by n-1 nested levels (usually with `concatAll()`)
45
- `concatMap()` n-1 nested with 1 `map()` as last nested operation is the functional version of a nested forEach
46
- `zip()` - useful flattening approach acting as a `map()` of two arrays at each index (even for len 1 arrays)
47

48
49
## Additional Helper Operators
- `take()` - updates n number of times (once by default) and then automatically unsubscribes
50
- `takeUntil()` - composes a source and stop observable where the latter results in exiting the former
51
- `throttle()` - delays updates
52
- `retry()` - retries n number of times on error (indefinitely by default)
53
- `doAction()` - ensure a synchronous side-effect occurs as part of an observable's `forEach()` execution
54
55
56
57
- `of()` - converts a value into an observable value
- `concat()` - concatenate n observables together
- `distinctUntilChanged()` - only `next()`s if previous value is not current value
- `scan()` - like `reduce()`, but emits the intermediate accumulations instead of one accumulated result
58
- `defer()` - a shortcut wrapper to creating a custom subscribe function
59

60
61
62
Subject (multi-cast observable)
- `share()` - enables one side effect to trigger many observers
- `replay()` - enables a side effect cache for late observer consumption
63
 
64
65
## Implementation Process
1. Think about any change as a collection of changes over time (a stream)
66
67
2. Abstract it to an Observable
3. Compose and operate over the stream(s)
68
4. Flatten the composition and implement the desired side-effect(s)
69
5. Ensure proper cleanup operator is used (if applicable)