Reactive Programming- Part I
Tags: rxjs, reactivex, javascript, programming
Published: 2024-03-05 17:32:05

#### ReactiveX

What is ReactiveX?
ReactiveX (Rx) is a programming paradigm and library that focuses on asynchronous programming using observable sequences.

Why to use ReactiveX?
ReactiveX provides API to treat and consume asynchronous stream of data originating from various sources e.g: Sensor readings, News Updates etc. in an concise and easy way, thus making it easy to develop and maintain asynchronous data.

#### Hot vs cold observables
Cold Observables
Sequences that start producing notifications per subscription. It starts producing data only when a subscriber subscribes to it.

Hot Observables
Sequence that starts producing notifications regardless of subscriptions. Subscribers that join later might miss some of the initial data.

#### Rxjs
RxJS is a implementation of Reactive Extensions (ReactiveX) for Javascript and TypeScript ReactiveX was developed by Erik Meijer at Microsoft. Open sourced in 2012 Used by GitHub, Netflix, SoundCloud, AirBnb and others Reactive Extensions is Cross Platform (available for Java, C#, C++, JavaScript, Python, and more)

#### Patterns used
- The Observer Pattern
- The Iterator Pattern
- Functional programming concepts

#### Observer Pattern
```javascript
// Observer
class Observer {
  constructor(name) {
    this.name = name;
  }
  
  update(message) {
    console.log(`${this.name} received message: ${message}`);
  }
}

// Subject
class Subject {
  constructor() {
    this.observers = [];
  }
  
  addObserver(observer) {
    this.observers.push(observer);
  }
  
  removeObserver(observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }
  
  notify(message) {
    this.observers.forEach(observer => observer.update(message));
  }
}

// Usage
const subject = new Subject();

const observer1 = new Observer("Observer 1");
const observer2 = new Observer("Observer 2");

subject.addObserver(observer1);
subject.addObserver(observer2);

subject.notify("Hello, observers!");
subject.removeObserver(observer2);
subject.notify("Observers after removal.");
```

#### Iterator Pattern
- The Iterator pattern is a behavioral design pattern that provides a way to access the elements of a collection (such as an array or list) without exposing the underlying representation. It allows you to traverse through the elements sequentially, abstracting away the details of how the traversal is implemented.

```javascript
// Iterator
class Iterator {
  constructor(collection) {
    this.collection = collection;
    this.index = 0;
  }

  hasNext() {
    return this.index < this.collection.length;
  }

  next() {
    if (this.hasNext()) {
      return this.collection[this.index++];
    }
    return null;
  }
}

// Collection
class Collection {
  constructor() {
    this.items = [];
  }

  addItem(item) {
    this.items.push(item);
  }

  getIterator() {
    return new Iterator(this.items);
  }
}

// Usage
const collection = new Collection();
collection.addItem("Item 1");
collection.addItem("Item 2");
collection.addItem("Item 3");

const iterator = collection.getIterator();

while (iterator.hasNext()) {
  console.log(iterator.next());
}
```

#### Key Functional Programming Concepts
1. Pure Functions: Pure functions are functions that always produce the same output for the same input and have no side effects. They don't modify data outside their scope or rely on external state. Pure functions promote predictability, testability, and composability.
2. Immutability: Functional programming encourages immutability, which means that once a data structure or value is created, it cannot be modified. Instead, new values are created when transformations or modifications are required. This avoids unintended side effects and makes reasoning about code easier.
3. Higher-Order Functions: In functional programming, functions are treated as first-class citizens. This means that functions can be assigned to variables, passed as arguments to other functions, and returned as results from functions. Higher-order functions are functions that either take other functions as arguments or return functions.
4. Function Composition: Functional programming promotes composing functions to create more complex behavior. Function composition involves chaining or combining multiple functions together to create a new function. It allows for modular and reusable code.
5. Recursion: Recursion is a technique where a function calls itself to solve a problem. Functional programming often relies on recursion instead of iterative loops. Recursive functions break down complex problems into simpler subproblems, which can lead to elegant and concise code.
6. Immutable Data Structures: Functional programming favors immutable data structures, such as lists, sets, and maps, that don't change after creation. Instead of modifying these data structures, functional programming encourages creating new instances with modifications. Immutable data structures provide safety and make it easier to reason about program behavior.
7. Referential Transparency: Referential transparency means that a function can be replaced with its resulting value without changing the program's behavior. It enables reasoning about code by allowing expressions to be evaluated independently of their context.

#### Operators
1. `map`: Transforms the items emitted by an Observable by applying a function to each item and returning the transformed result.

2. `filter`: Filters items emitted by an Observable based on a predicate function, allowing only those items that satisfy the condition to pass through.

3. `mergeMap` (also known as `flatMap`): Projects each source item to an Observable and merges the emissions from those Observables into a single stream. It allows for concurrent inner subscriptions.

4. `switchMap`: Projects each source item to an Observable, cancels the previous inner Observable (if any), and emits the values from the most recent inner Observable. Useful for scenarios like canceling ongoing requests when a new request is made.

5. `concatMap`: Projects each source item to an Observable and concatenates the emissions from those Observables, ensuring that the order of emissions is preserved.
6. `debounceTime`: Emits a value from the source Observable only after a specified amount of silence, ignoring any new values emitted during that period. Useful for scenarios like search input throttling.

7. `distinctUntilChanged`: Emits a value from the source Observable only if it is different from the previous value. It ensures that only distinct consecutive values are emitted.

8. `takeUntil`: Emits values from the source Observable until another Observable emits a value. After that, it completes and stops emitting further values.

9. `retry`: Resubscribes to the source Observable if it encounters an error, allowing for a specified number of retries.

10. `catchError` (or `catch`): Catches errors emitted by the source Observable and substitutes them with another Observable or a default value, allowing graceful error handling.