RxJS เบื้องต้น: Observable, Subject, BehaviorSubject

RxJS คือ Library สำหรับจัดการข้อมูลแบบ Stream ใน JavaScript ส่วน Observable คือแหล่งข้อมูลที่ปล่อยค่าออกมาเป็นลำดับและให้ผู้สนใจ Subscribe เพื่อรับค่าได้

Timeline/ประวัติศาสตร์

%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#fabd2f", "primaryTextColor": "#282828", "primaryBorderColor": "#b57614", "lineColor": "#7c6f64", "secondaryColor": "#83a598", "tertiaryColor": "#b8bb26", "background": "#fbf1c7", "mainBkg": "#ebdbb2", "fontFamily": "Tahoma, sans-serif"}}}%%
flowchart LR
  subgraph Era1["ยุค Callback / Callback Era"]
    A["callback()
รับค่าครั้งเดียวหรือซ้อนกัน"] end subgraph Era2["ยุค Promise / Promise Era"] B["Promise
ผลลัพธ์ครั้งเดียวในอนาคต"] end subgraph Era3["ยุค Stream / Observable Era"] C["Observable
หลายค่าตามเวลา"] D["Subject/BehaviorSubject
Multicast และ State"] end A --> B --> C --> D

แนวคิดสำคัญ

ตารางเปรียบเทียบ Observable

ชนิด มีค่าเริ่มต้น ส่งค่าล่าสุดให้ Subscriber ใหม่ เหมาะกับ
Observable ไม่จำเป็น ไม่เสมอ HTTP, Event, Stream
Subject ไม่มี ไม่ Event bus
BehaviorSubject มี ใช่ Shared State
Promise ไม่มี Stream ไม่เกี่ยวข้อง งาน async ผลลัพธ์ครั้งเดียว

Mermaid Diagram: Observable Stream

%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#fabd2f", "primaryTextColor": "#282828", "primaryBorderColor": "#b57614", "lineColor": "#7c6f64", "secondaryColor": "#83a598", "tertiaryColor": "#b8bb26", "background": "#fbf1c7", "mainBkg": "#ebdbb2", "fontFamily": "Tahoma, sans-serif"}}}%%
flowchart LR
  A["Observable
แหล่งข้อมูล"] --> B["next
ส่งค่า"] B --> C["Observer
ผู้รับค่า"] C --> D["Subscription
การติดตาม"] D --> E["unsubscribe
หยุดรับค่า"]

สมการคณิตศาสตร์: Subscription ที่ยังค้าง

M = S - U

Code Example

// counter.component.ts
// ตัวอย่าง Observable, Subscription และการ unsubscribe
import { Component, OnDestroy, OnInit } from '@angular/core';
import { BehaviorSubject, interval, Subscription } from 'rxjs';

@Component({
  selector: 'app-counter',
  template: `
    <p>Counter: {{ count }}</p>
    <button (click)="setStatus('running')">Set Status</button>
  `
})
export class CounterComponent implements OnInit, OnDestroy {
  count = 0;
  private sub?: Subscription;
  private statusSubject = new BehaviorSubject<string>('idle');
  status$ = this.statusSubject.asObservable();

  ngOnInit(): void {
    this.sub = interval(1000).subscribe(value => {
      this.count = value;
    });
  }

  setStatus(status: string): void {
    this.statusSubject.next(status);
  }

  ngOnDestroy(): void {
    this.sub?.unsubscribe();
  }
}

// ตัวอย่างการใช้งาน:
// <app-counter></app-counter>

กิจกรรมท้ายบท

  1. สร้าง Observable ด้วย interval(1000)
  2. แสดงค่าตัวเลขใน Component
  3. unsubscribe ใน ngOnDestroy
  4. ทดลองเปลี่ยนจาก Subject เป็น BehaviorSubject

กลับสัปดาห์ที่ 8