Skip to content

Instantly share code, notes, and snippets.

@RoyalIcing
Last active September 3, 2021 00:21
Show Gist options
  • Save RoyalIcing/6094551d35a1e04744fb7168ad003f62 to your computer and use it in GitHub Desktop.
Save RoyalIcing/6094551d35a1e04744fb7168ad003f62 to your computer and use it in GitHub Desktop.
RxJS Loading Patterns

RxJS Loading Patterns

Observable creators

of(A...) => Observable<A>

Create an observable emitting each item passed in.

from(Promise<A> | Iterable<A>) => Observable<A>

Create an observable from a Promise or Iterable.

merge(Observable<A>, Observable<B>, Observable<C>...) => Observable<A | B | C...>

Emit whatever and whenever the passed observables emit.

combineLatest([Observable<A>, Observable<B>, Observable<C>...]) => Observable<[A, B, C...]>

Emit with the value for each observable in its corresponding position in an array.

Wait for all passed observable to emit once — i.e. array won’t have any holes with missing values.

EMPTY

A constant observable that does not emit and immediately completes. Sometimes useful for composing with operators.

Operators for transforming

map

Transform from one value to another value.

Your function: (A) => B Observable: Observable<A> --> Observable<B>

filter

Ignore events you don’t want.

Your function: (A) => boolean Observable: Observable<A> --> Observable<A>

scan

Combine two adjacent events into a summarised value. e.g. sum a total

Your function: (B, A) => B Observable: Observable<A> --> Observable<B>

switchMap

Transform from a value to a brand new observable.

Your function: (A) => Observable<B> Observable: Observable<A> --> Observable<B>

catchError

Recover an error into a brand new observable.

Your function: (Error) => Observable<B> Observable: Observable<A> --> Observable<B>

Other useful operators

take(number)

Ensures the observable completes after n number of values have been emitted.

timeoutWith(number, replacementObservable)

Fail or recover if the observable doesn’t emit within a time period.

shareReplay({ bufferSize: 1, refCount: true })

  • New subscribers will share the same underlying source observable (instead of say making a new HTTP request for each subscriber)
  • by caching the last emitted value (bufferSize: 1)
  • and will tear down that source observable when there are no more subscribers left (refCount: true)

tap(callback)

Allow you to perform side effects using the emitted events, such as logging or updating some external state. Best to use this sparingly.


Difference between Promises and Observables

const promise = fetch("/api");

promise.then(() => console.log("loaded"));
promise.then(() => console.log("loaded"));

// How many HTTP requests are made?
const o$ = httpClient.get("/api");

o$.subscribe(() => console.log("loaded"));
o$.subscribe(() => console.log("loaded"));

// How many HTTP requests are made?
  • A promise either resolves (happy path), or rejects (sad path). It can’t do both.
  • An observable emit zero, one, or many times (happy path).
  • An observable may complete, which means it stop emitting events and all subscribers are unsubscribed.
  • An observable may error (sad path), which means it also completes and stops emitting events.
  • A promise represents some future value. If you have a promise, then it has already started fetching, loading, etc.
  • An observable represents many futures values. If have an observable, then it might have started fetching/loading/listening or it might not have.

Mental model of observables

You chain observables together to declare the conveyor belt for how values will flow in your application.

They flow starting from external sources (user interactions, route params, timers) through external systems (HTTP communication with APIs, local caches) and back out.

The final result of your chain will often be some sort of value to present to a user (e.g. a view model of the current state from the API).

You can think of declaring a chain of observable as being similar to declaring a class. A class only becomes alive when you instatiate an instance of it — an object. And you can instantiate multiple objects of that single class you defined.

Similarly, a chain of observables usually only becomes alive when you subscribe to it. And a single chain can have one or many subscribers.

Anti-Patterns

  • combineLatest with circular reference to inputs. Instead use a closure with a nested .pipe.
  • Defensive shareReplay everywhere.
  • BehaviorSubject everywhere. Don’t treat subjects as mutable variables.
  • tap that does a lot of work.

Patterns to focus more on:

  • Observables model when as well as what. If two pieces of state change together, perhaps they should be the same observable or same subject?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment