reboottime / Angular-Playbook

Crash angular for interview, now it's a repository works as a Angular playbook.
2 stars 0 forks source link

RxJs and Reactive Programming #14

Open reboottime opened 7 months ago

reboottime commented 7 months ago

Overview

Introduction

This article attempts to seize Rxjs essentials in a short time. The content covers two parts:


As RxJS declares itself as a library for reactive programming using Observables, this article starts with understanding reactive programming essentials.


Quick References

reboottime commented 7 months ago

Reactive Programming, Part I: Core Concepts

Definition and Application Scenarios


Core Concepts

These principles facilitate the creation of side effect-free and scalable applications.

reboottime commented 7 months ago

Reactive Programming, Part II: Observer Pattern

The concepts and their associated code patterns discussed below were learned from Derek Banas's tutorial on the Observer Design Pattern, available at this link.

Core concepts and application of Observer Pattern

image


Sample code of Observer Pattern (using Typescript)

interface Subject { attach: (observer: Observer) => void; detach: (observer: Observer) => void; notifyObservers: () => void; }


- Subject code
```ts

class ConcreteSubject implements Subject {
    private observers: Observer[] = [];

    // Any state changes that the observers should know about
    private state: any;

    public attach(observer: Observer): void {
        const isExist = this.observers.includes(observer);
        if (isExist) {
            return console.log('Subject: Observer has been attached already.');
        }

        console.log('Subject: Attached an observer.');
        this.observers.push(observer);
    }

    public detach(observer: Observer): void {
        const observerIndex = this.observers.indexOf(observer);
        if (observerIndex === -1) {
            return console.log('Subject: Nonexistent observer.');
        }

        this.observers.splice(observerIndex, 1);
        console.log('Subject: Detached an observer.');
    }

    public notifyObservers(): void {
        console.log('Subject: Notifying observers...');
        for (const observer of this.observers) {
            observer.notify(this.state);
        }
    }

    // Usually, the subscription logic is only a fraction of what a Subject can do.
    // Subjects commonly hold some important business logic, that triggers a notification
    // method whenever something important is about to happen (or after it).
    public someBusinessLogic(): void {
        console.log('\nSubject: I\'m doing something important.');
        this.state = Math.floor(Math.random() * 10);

        console.log(`Subject: My state has just changed to: ${this.state}`);
        this.notifyObservers();
    }
}
class ConcreteObserver implements Observer {
    // Receive update from subject
    public notify(data: any): void {
        console.log(`Observer: Reacted to the event with data: ${data}`);
    }
}

const observer1 = new ConcreteObserver(); subject.attach(observer1);

const observer2 = new ConcreteObserver(); subject.attach(observer2);

subject.someBusinessLogic(); subject.someBusinessLogic();

subject.detach(observer2);

subject.someBusinessLogic();

reboottime commented 7 months ago

RxJs, Part I: Terminologies

Terminologies in Rxjs:


Take the burger shop (as in this tutorial) as an analogy:

reboottime commented 7 months ago

RxJs, Part II: Code Pattern


const { Observable } = require("rxjs");
const { pluck, map, filter } = require("rxjs/operators");

const users = {
  data: [
    {
      id: 1,
      status: "active",
      age: 14,
    },
    {
      id: 1,
      status: "inactive",
      age: 12,
    },
    {
      id: 1,
      status: "active",
      age: 42,
    },
    {
      id: 1,
      status: "inactive",
      age: 42,
    },
    {
      id: 1,
      status: "active",
      age: 13,
    },
    {
      id: 1,
      status: "inactive",
      age: 75,
    },
    {
      id: 1,
      status: "inactive",
      age: 43,
    },
    {
      id: 1,
      status: "inactive",
      age: 54,
    },
    {
      id: 1,
      status: "active",
      age: 7,
    },
    {
      id: 1,
      status: "active",
      age: 17,
    },
  ],
};

const observable = new Observable((subscriber) => {
  subscriber.next(users);
}).pipe(
  pluck("data"),
  filter((users) => users.length >= 10),
  map((users) => {
    return users.filter((user) => user.status === "active");
  }),
  map((users) => {
    return users.reduce((sum, user) => sum + user.age, 0) / users.length;
  }),
  map((average) => {
    if (average < 18) throw new Error(`Average age is too small (${average})`);
    else return average;
  }),
  map((average) => `The average age is ${average}`)
);

const observer = {
  next: (x) => console.log("Observer got a next value: " + x),
  error: (err) => console.error("Observer got an error: " + err),
  complete: () => console.log("Observer got a complete notification"),
};
const observer2 = {
  next: (x) => console.log("Observer 2 got a next value: " + x),
  error: (err) => console.error("Observer 2 got an error: " + err),
  complete: () => console.log("Observer 2 got a complete notification"),
};

observable.subscribe(observer);

observable.subscribe(observer2);
reboottime commented 7 months ago

References