ReplaySubject: RxJS 中的「時間記憶者」
什麼是 ReplaySubject?
ReplaySubject 是 RxJS 中的一種特殊型態 Subject,可以想像成一個「帶有記憶功能的訊息傳遞者」。它能記住之前發送過的一定數量的值,並在新的訂閱者訂閱時,自動將這些值重新發送給新訂閱者。
簡單比喻
想像 ReplaySubject 是一個教室的監視錄影系統:
系統會不斷錄製教室中發生的事情(發出的值)
系統可以設定儲存最近 N 小時的錄影(記憶的值數量)
當有新學生(新訂閱者)進入教室時,系統會先播放錄好的內容,讓新學生了解之前發生了什麼,然後繼續實時播放新內容
與其他 Subject 的區別
Subject
只發送訂閱後的新事件
BehaviorSubject
立即給新訂閱者最後一個值,然後發送新事件
ReplaySubject
給新訂閱者發送指定數量的歷史值,然後發送新事件
AsyncSubject
只在完成時發送最後一個值
ReplaySubject 基本用法
建立 ReplaySubject
// 建立一個會記住最後 2 個值的 ReplaySubject
const replay = new ReplaySubject<number>(2);
// 也可以加入時間限制(只重播 500 毫秒內的值)
const timeReplay = new ReplaySubject<number>(2, 500);
發送與訂閱
const replay = new ReplaySubject<number>(3); // 記住最後 3 個值
// 發送一些值
replay.next(1);
replay.next(2);
replay.next(3);
replay.next(4);
// 第一位訂閱者會收到最後 3 個值:2, 3, 4
replay.subscribe(value => {
console.log('訂閱者 1:', value);
});
// 再發送一個值
replay.next(5);
// 第二位訂閱者會收到:3, 4, 5
replay.subscribe(value => {
console.log('訂閱者 2:', value);
});
實際應用場景
1. 使用者操作的歷史記錄
需要記住使用者最近的幾個操作,以便提供「復原」功能:
// 記住最後 10 個操作
const userActions = new ReplaySubject<UserAction>(10);
function performAction(action: UserAction) {
// 執行操作
action.execute();
// 記錄操作以便復原
userActions.next(action);
}
// 復原功能可以訂閱並取得最近的操作
2. 處理延遲載入的元件
確保元件無論何時被載入,都能獲取最新狀態:
// 全域服務中的狀態管理
@Injectable({ providedIn: 'root' })
export class AppStateService {
// 記住最後 1 個應用程式狀態
private appState = new ReplaySubject<AppState>(1);
// 元件可以訂閱獲取狀態
getState() {
return this.appState.asObservable();
}
// 更新狀態
updateState(newState: AppState) {
this.appState.next(newState);
}
}
3. 表單元件
例如在 Select 元件中處理選項過濾:
@Component({...})
export class SelectComponent {
@Input() options: Option[] = [];
// 過濾後的選項
filteredOptions = new ReplaySubject<Option[]>(1);
ngOnInit() {
// 初始化選項
this.filteredOptions.next(this.options);
}
// 搜尋過濾處理
onSearch(term: string) {
const filtered = this.options.filter(
option => option.label.includes(term)
);
this.filteredOptions.next(filtered);
}
}
適合使用 ReplaySubject 的時機
當你遇到以下情況時,考慮使用 ReplaySubject:
需要給延遲訂閱者提供之前的值:例如延遲載入的元件需要之前的系統狀態
需要記住特定數量的歷史事件:例如操作記錄、最近的輸入歷史等
處理非同步初始化:當訂閱可能發生在數據初始化之前或之後,都需要保證訂閱者能得到數據
解決競態條件:多個元件訂閱同一個數據源,需要確保所有元件都能收到相同的初始值
實現「快照」功能:需要在特定時刻捕獲並持久化系統的狀態
需要注意的事項
ReplaySubject 會在記憶體中保存歷史值,如果值很大或數量很多,可能會導致記憶體問題
如果只需要最後一個值,考慮使用 BehaviorSubject
可以指定時間限制,只重播特定時間範圍內的值
完成(complete)後,ReplaySubject 會維持最後的狀態,但不再接收新值
總結
ReplaySubject 是一個強大的「時間記憶者」,它記住過去發生的事件,讓新訂閱者不會錯過重要資訊。在需要處理歷史數據、解決訂閱時機問題、或實現資料快照功能時,ReplaySubject 是一個很好的選擇。
Last updated