Khi làm việc với RxJS khái niệm đầu tiên anh em cần biết đó là Observable là gì ? Có bao nhiêu loại Observable và cách sử dụng chúng sao cho đúng cách nhất.

Các loại Observable trong Angular (RxJS)


Trong RxJS (Reactive Extensions for JavaScript), Observable là một kiểu đối tượng quan trọng dùng để quản lý các luồng dữ liệu bất đồng bộ (asynchronous data streams). Có nhiều loại Observable khác nhau, tùy thuộc vào cách sử dụng và quản lý dữ liệu. Dưới đây là các loại Observable phổ biến và cách sử dụng của chúng trong Angular.


1️⃣ Hot Observable


Hot Observable là những observable mà dữ liệu được phát sinh ngay cả khi không có observer (người theo dõi). Điều này có nghĩa là dữ liệu có thể được phát sinh và phát đi từ Observable mà không cần phải đợi một subscriber đăng ký. Nó giống như một broadcast (phát sóng) được chia sẻ giữa tất cả các observers.


Ví dụ: Sự kiện từ DOM, như chuột click, hoặc các luồng dữ liệu như WebSocket, HTTP request.


Đặc điểm:


  • Dữ liệu được phát sinh ngay cả khi không có subscriber.


  • Các subscriber có thể nhận được dữ liệu từ điểm hiện tại mà không phải là toàn bộ luồng dữ liệu trước đó.


Ví dụ về Hot Observable:


import { Observable } from 'rxjs';

const hotObservable = new Observable(subscriber => {
  setInterval(() => {
    subscriber.next(Math.random());
  }, 1000);
});

hotObservable.subscribe(data => console.log(`Subscriber 1: ${data}`));
setTimeout(() => {
  hotObservable.subscribe(data => console.log(`Subscriber 2: ${data}`));
}, 3000);


Output:

Subscriber 1: 0.23
Subscriber 1: 0.85
Subscriber 1: 0.67
Subscriber 2: 0.45  <-- Subscriber 2 bắt đầu nhận từ đây
Subscriber 1: 0.45
Subscriber 1: 0.78
Subscriber 2: 0.78
...


Giải thích


  • Subscriber 1 nhận được tất cả các giá trị ngay khi Observable bắt đầu phát ra.


  • Subscriber 2 chỉ nhận được giá trị từ thời điểm nó đăng ký (sau 3 giây). Nó không nhận được giá trị cũ (0.23, 0.85, 0.67) vì Observable này không lưu trạng thái trước đó.


  • Mỗi lần subscriber.next(Math.random()) chạy, cả hai subscriber sẽ nhận cùng một giá trị, nhưng Subscriber 2 chỉ bắt đầu nhận từ khi nó đăng ký.


2️⃣ Cold Observable


Cold Observable là những observable mà dữ liệu chỉ được phát sinh khi có ít nhất một subscriber đăng ký vào nó. Mỗi khi có một observer, Cold Observable sẽ bắt đầu phát lại dữ liệu từ đầu cho từng subscriber. Điều này giống như một stream riêng biệt cho mỗi subscriber.


Ví dụ: HTTP requests, file đọc/ghi, các thao tác với database, v.v.


Đặc điểm:


  • Dữ liệu chỉ phát sinh khi có subscriber.


  • Mỗi subscriber sẽ nhận được dữ liệu như một luồng riêng biệt, bắt đầu từ điểm đầu tiên (ví dụ: với HTTP request, mỗi subscriber nhận một request riêng biệt).


Ví dụ về Cold Observable:


import { Observable } from 'rxjs';

const coldObservable = new Observable(subscriber => {
  console.log('HTTP request starts');
  subscriber.next('Hello');
  subscriber.complete();
});

coldObservable.subscribe(data => console.log(`Subscriber 1: ${data}`));
coldObservable.subscribe(data => console.log(`Subscriber 2: ${data}`));


Output:

HTTP request starts
Subscriber 1: Hello
HTTP request starts
Subscriber 2: Hello


Giải thích


Khi Subscriber 1 đăng ký:

  • Observable bắt đầu thực thi.
  • "HTTP request starts" được in ra console.
  • "Hello" được phát và Subscriber 1 nhận được.
  • Observable kết thúc (complete()).


Khi Subscriber 2 đăng ký:

  • Observable chạy lại từ đầu.
  • "HTTP request starts" được in ra một lần nữa.
  • "Hello" được phát và Subscriber 2 nhận được.
  • Observable kết thúc.


3️⃣ Multicast Observable (Subject)


Một Multicast Observable là một kiểu observable đặc biệt, trong đó một giá trị duy nhất được phát sóng tới tất cả các observer cùng một lúc. Subject là một ví dụ điển hình của một Multicast Observable.


Ví dụ: Sử dụng Subject để chia sẻ một luồng dữ liệu giữa nhiều subscriber.


Đặc điểm:


  • Subject có thể phát dữ liệu tới nhiều subscriber cùng một lúc.


  • Subscriber sẽ nhận được cùng một giá trị từ Subject khi nó phát dữ liệu.


Ví dụ về Subject (Multicast Observable):


import { Subject } from 'rxjs';

// Tạo một Subject
const subject = new Subject();

// Subscriber 1
subject.subscribe(data => console.log(`Subscriber 1: ${data}`));

// Gửi giá trị cho tất cả subscribers
subject.next('Hello');

// Subscriber 2
subject.subscribe(data => console.log(`Subscriber 2: ${data}`));

// Gửi thêm giá trị mới
subject.next('World');


Output:

Subscriber 1: Hello
Subscriber 1: World
Subscriber 2: World


Giải thích


Khi Subscriber 1 đăng ký, nó nhận được dữ liệu "Hello" và "World". Subscriber 2 chỉ nhận được dữ liệu "World" vì chúng ta gọi next('World') sau khi Subscriber 2 đăng ký. Subject phát dữ liệu tới tất cả subscriber đã đăng ký tại thời điểm phát dữ liệu.


4️⃣ ReplaySubject


ReplaySubject là một loại Subject đặc biệt, có thể phát lại một số giá trị trước đó cho các subscriber mới. Điều này có nghĩa là khi một subscriber mới đăng ký, ReplaySubject sẽ phát lại một số giá trị mà nó đã phát trước đó.


Ví dụ: Dùng khi muốn phát lại các giá trị trước đó mà không cần phải bắt đầu lại từ đầu.


Đặc điểm:


  • Có thể lưu lại một số giá trị (hoặc tất cả) mà nó đã phát.


  • Các subscriber mới nhận được các giá trị mà ReplaySubject đã phát trước đó.


Ví dụ về ReplaySubject:


import { ReplaySubject } from 'rxjs';

const replaySubject = new ReplaySubject(2);  // Lưu lại 2 giá trị trước đó

replaySubject.next('A');
replaySubject.next('B');
replaySubject.next('C');

replaySubject.subscribe(data => console.log(`Subscriber 1: ${data}`));  // Nhận 'B', 'C'

replaySubject.subscribe(data => console.log(`Subscriber 2: ${data}`));  // Nhận 'B', 'C'


Output:

Subscriber 1: B
Subscriber 1: C
Subscriber 2: B
Subscriber 2: C


Giải thích


  • ReplaySubject(2) được tạo và được thiết lập để lưu 2 giá trị gần nhất.


  • Gọi next('A'), next('B'), next('C').


  • 'A' bị loại bỏ vì ReplaySubject(2) chỉ lưu 2 giá trị gần nhấtLưu: ['B', 'C'].


  • Subscriber 1 đăng ký: Ngay lập tức nhận 2 giá trị gần nhất (B, C)


  • Subscriber 2 đăng ký: Cũng nhận 2 giá trị gần nhất (B, C).




5️⃣ BehaviorSubject


BehaviorSubject là một loại Subject đặc biệt khác, luôn luôn giữ một giá trị cuối cùng, và khi có một subscriber mới, nó sẽ nhận giá trị đó ngay lập tức. Nó yêu cầu một giá trị mặc định khi khởi tạo.


Ví dụ: Dùng để quản lý trạng thái hoặc lưu trữ giá trị cuối cùng của một chuỗi sự kiện.


Đặc điểm:


  • Lưu trữ giá trị cuối cùng.


  • Các subscriber mới sẽ nhận được giá trị cuối cùng ngay lập tức.


Ví dụ về BehaviorSubject:


import { BehaviorSubject } from 'rxjs';

const behaviorSubject = new BehaviorSubject('Initial Value');  // Giá trị mặc định

behaviorSubject.subscribe(data => console.log(`Subscriber 1: ${data}`));  // Nhận 'Initial Value'

behaviorSubject.next('New Value');  // Cập nhật giá trị

behaviorSubject.subscribe(data => console.log(`Subscriber 2: ${data}`));  // Nhận 'New Value'


Output:

Subscriber 1: Initial Value
Subscriber 1: New Value
Subscriber 2: New Value


Giải thích


  • Tạo BehaviorSubject với giá trị mặc định là 'Initial Value'.


  • Subscriber 1 đăng ký ngay lập tức, nên nó nhận được giá trị 'Initial Value'.


  • Gọi next('New Value'), cập nhật giá trị trong BehaviorSubject.


  • Subscriber 1 nhận được 'New Value'.


  • Subscriber 2 đăng ký sau đó, và ngay lập tức nhận được giá trị mới nhất 'New Value'.



Tóm tắt các loại Observable trong Angular/RxJS



Kết luận


Các loại Observable trong Angular/RxJS có cách thức và ứng dụng khác nhau, tùy thuộc vào mục đích của bạn:


  • Hot Observable thường dùng cho các luồng dữ liệu liên tục.


  • Cold Observable dùng khi bạn muốn tạo một observable mà mỗi subscriber nhận được một dữ liệu riêng biệt.


  • Subject, ReplaySubject và BehaviorSubject đều là các kiểu multicast giúp phát dữ liệu tới nhiều subscribers, nhưng có sự khác biệt trong việc lưu trữ và phát lại giá trị.