# RxJS di Angular 2026: Operator, Subject, dan Interop Signals
> RxJS di Angular 2026: kuasai operator, Subject, dan pola interop Signals yang dipakai developer Angular di produksi, plus pertanyaan wawancara yang paling sering muncul.
- Published: 2026-07-02
- Updated: 2026-07-07
- Author: SharpSkill
- Tags: angular, rxjs, signals, observables, typescript, deep-dive
- Reading time: 10 min
---
RxJS di Angular tetap menjadi tulang punggung pemrograman asinkron pada tahun 2026, bahkan ketika Signals mengubah cara komponen Angular melacak state sinkron. Mengetahui kapan harus memilih Observable, kapan Signal lebih cocok, dan bagaimana keduanya saling beroperasi kini menjadi keterampilan inti bagi setiap developer Angular. Pembahasan mendalam ini menelusuri operator, Subject, dan pola interop yang membedakan jawaban wawancara yang meyakinkan dari yang tersendat.
> **Signals vs Observables dalam satu kalimat**
>
> Signals memodelkan state sinkron yang selalu memiliki nilai saat ini; Observables memodelkan aliran event asinkron sepanjang waktu. Angular 20 mempertahankan keduanya, dan paket `@angular/core/rxjs-interop` adalah jembatan resmi di antara keduanya.
## Mengapa RxJS tetap penting berdampingan dengan Angular Signals
Kehadiran Signals membuat banyak tim bertanya apakah RxJS akan segera ditinggalkan. Jawabannya, tiga versi mayor kemudian, adalah tidak. Signals unggul dalam merepresentasikan state yang memiliki nilai saat ini: sebuah penghitung, sebuah field formulir, sebuah tab yang dipilih. Observables unggul dalam memodelkan event yang tiba sepanjang waktu dan mungkin perlu ditransformasi, dibatalkan, atau digabungkan: respons HTTP, pesan WebSocket, input keyboard, event router, dan timer.
API bawaan Angular sendiri membuat pembagian ini terlihat jelas. `HttpClient` masih mengembalikan Observables. Reactive forms mengekspos `valueChanges` dan `statusChanges` sebagai Observables. Event router mengalir melalui sebuah Observable. Di mana pun beberapa event asinkron mengalir melalui kanal yang sama, operator RxJS tetap menjadi alat paling ekspresif yang tersedia. [Panduan reaktivitas Angular](https://angular.dev/guide/signals/rxjs-interop) resmi memperlakukan kedua model tersebut sebagai saling melengkapi, bukan bersaing, dan [dokumentasi RxJS](https://rxjs.dev/guide/overview) tetap menjadi rujukan wajib untuk setiap aliran data yang tidak sepele.
Aturan praktis yang bertahan di code review: gunakan Signals untuk state komponen lokal yang dibaca di template, dan gunakan Observables untuk pipeline asinkron yang membutuhkan operator. Lapisan interop menghubungkan keduanya sehingga tidak ada model yang menjadi tembok penghalang.
Operator kombinasi memperkuat argumen ini. Mengoordinasikan beberapa sumber asinkron, seperti menggabungkan parameter rute dengan filter tersimpan milik pengguna dan feed harga langsung, adalah persis untuk apa `combineLatest`, `withLatestFrom`, dan `forkJoin` dibangun. Signals dapat menurunkan nilai terkomputasi dari signal lain, tetapi tidak dapat mengekspresikan koordinasi berbasis waktu seperti "hanya pancarkan setelah ketiga sumber menghasilkan setidaknya satu nilai." Komposisi deklaratif sepanjang waktu itulah alasan abadi mengapa RxJS tetap ada di dalam kotak peralatan.
## Operator RxJS inti yang diandalkan setiap developer Angular
Operator adalah fungsi murni yang mengambil sebuah Observable dan mengembalikan yang baru. Skenario wawancara klasik adalah search-as-you-type, karena ia melatih empat operator sekaligus: membatasi laju input, melewati duplikat, membatalkan permintaan usang, dan pulih dari kesalahan.
```typescript
// search.component.ts
import { Component, inject } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { debounceTime, distinctUntilChanged, switchMap, catchError } from 'rxjs/operators';
import { of } from 'rxjs';
@Component({
selector: 'app-search',
imports: [ReactiveFormsModule],
template: ``,
})
export class SearchComponent {
private http = inject(HttpClient);
query = new FormControl('');
results$ = this.query.valueChanges.pipe(
debounceTime(300), // wait until typing pauses for 300ms
distinctUntilChanged(), // ignore emissions that repeat the last value
switchMap((term) => // cancel the in-flight request, start a fresh one
this.http.get(`/api/search?q=${term}`).pipe(
catchError(() => of([])) // recover from a failed request without killing the stream
)
)
);
}
```
Detail halus yang diselidiki pewawancara adalah mengapa `switchMap` berada di level terluar sementara `catchError` bersarang di dalamnya. Menempatkan `catchError` pada Observable HTTP di dalam berarti permintaan yang gagal mengembalikan array kosong dan aliran terluar tetap mengalir. Memindahkan `catchError` ke pipe terluar akan menghentikan seluruh pencarian setelah kesalahan pertama, merusak setiap ketukan tombol berikutnya. Keempat operator perataan masing-masing menjawab pertanyaan konkurensi yang berbeda, dan memilih yang salah adalah salah satu sumber race condition paling umum di aplikasi Angular.
> **Memilih operator perataan**
>
> Gunakan `switchMap` untuk membatalkan permintaan sebelumnya dan hanya mempertahankan yang terbaru (pencarian typeahead). Gunakan `mergeMap` untuk menjalankan setiap permintaan secara bersamaan (unggahan paralel yang independen). Gunakan `concatMap` untuk mengantre permintaan dalam urutan yang ketat (penulisan berurutan yang tidak boleh tumpang tindih). Gunakan `exhaustMap` untuk mengabaikan input baru sampai yang sekarang selesai (memblokir tombol submit yang diklik ganda).
Memilih dengan tepat inilah yang mengubah fitur yang labil menjadi fitur yang dapat diprediksi. [Modul operator RxJS SharpSkill](/technologies/angular/interview-questions/rxjs-operators) mengasah perbedaan ini dengan pertanyaan berjenjang yang mereproduksi race condition persis yang dicegah oleh masing-masing operator.
## Subject dan multicasting: BehaviorSubject, ReplaySubject, dan Subject
Sebuah Observable biasa bersifat unicast: setiap subscriber memicu eksekusi baru. Sebuah Subject adalah sekaligus Observable dan Observer, yang membuatnya multicast. Properti itu menjadikan Subject cara standar untuk membangun state store ringan atau event bus di dalam sebuah service.
Ketiga varian berbeda dalam apa yang mereka putar ulang kepada subscriber yang terlambat. Sebuah `Subject` biasa tidak memancarkan apa pun dari masa lalu, sehingga subscriber hanya melihat nilai yang tiba setelah ia berlangganan. Sebuah `BehaviorSubject` menyimpan nilai saat ini dan segera memutarnya ulang, itulah mengapa ia membutuhkan nilai awal. Sebuah `ReplaySubject` menyangga sejumlah pancaran masa lalu yang dapat dikonfigurasi dan memutar ulang semuanya.
```typescript
// cart.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class CartService {
// BehaviorSubject holds the latest value and replays it to new subscribers
private readonly itemsSubject = new BehaviorSubject([]);
// expose a read-only stream, never the writable Subject itself
readonly items$ = this.itemsSubject.asObservable();
add(item: string): void {
const current = this.itemsSubject.value; // synchronous access to the last value
this.itemsSubject.next([...current, item]); // emit the next immutable state
}
}
```
Dua kebiasaan penting di sini. Pertama, mengekspos `asObservable()` alih-alih Subject itu sendiri mencegah kode luar memanggil `next()` dan mengubah state melalui pintu belakang. Kedua, memancarkan referensi array baru alih-alih memutasinya di tempat menjaga change detection dan pemeriksaan kesetaraan tetap dapat diprediksi. Pola ini sudah ada sebelum Signals dan masih bekerja dengan baik untuk state bersama yang dapat diinjeksi yang dikonsumsi oleh banyak komponen tak terkait.
Meskipun demikian, sebuah `BehaviorSubject` yang digunakan murni untuk menyimpan satu nilai saat ini yang dibaca di template kini sering kali lebih baik diekspresikan sebagai Signal yang dapat ditulis, yang menghilangkan langganan dan formalitas `asObservable()`. Subject tetap menjadi pilihan yang lebih kuat ketika store juga perlu mentransformasi, men-debounce, atau menggabungkan pancarannya dengan aliran lain, karena operator tersebut tidak memiliki padanan Signal langsung. Memutuskan di antara keduanya per kasus penggunaan, alih-alih selalu menggunakan salah satunya di mana-mana, adalah tanda kefasihan yang dihargai pewawancara.
## Interop RxJS dan Signals dengan toSignal dan toObservable
Paket interop menyediakan dua fungsi utama. `toSignal` mengonversi sebuah Observable menjadi Signal hanya-baca yang dapat dikonsumsi langsung oleh template, dan ia mengelola siklus hidup langganan secara otomatis. `toObservable` melakukan sebaliknya, mengubah sebuah Signal menjadi Observable sehingga operator RxJS dapat memproses perubahannya.
```typescript
// dashboard.component.ts
import { Component, signal, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { toSignal, toObservable } from '@angular/core/rxjs-interop';
import { switchMap } from 'rxjs/operators';
@Component({ selector: 'app-dashboard', template: `{{ user()?.name }}` })
export class DashboardComponent {
private http = inject(HttpClient);
// a Signal drives the query; toObservable turns each change into a stream event
readonly userId = signal(1);
private readonly user$ = toObservable(this.userId).pipe(
switchMap((id) => this.http.get<{ name: string }>(`/api/users/${id}`))
);
// toSignal subscribes immediately and unsubscribes on destroy, no manual teardown
readonly user = toSignal(this.user$, { initialValue: null });
}
```
Opsi `initialValue` layak diperhatikan. Tanpanya, `toSignal` mengembalikan Signal yang bertipe nilai atau `undefined`, karena belum ada yang dipancarkan. Meneruskan `initialValue: null` memberi template state awal yang terdefinisi dan tipe yang lebih bersih. Untuk sumber sinkron seperti `BehaviorSubject`, `requireSync: true` menjamin sebuah nilai tersedia pada pembacaan pertama dan menghapus `undefined` dari tipe sepenuhnya. Jembatan dua arah inilah yang memungkinkan basis kode mengadopsi Signals secara bertahap tanpa menulis ulang setiap pipeline Observable yang sudah ada. [Modul wawancara Angular Signals](/technologies/angular/interview-questions/angular-signals) pendamping membahas model reaktivitas secara mendalam, dan [ikhtisar fitur Angular 18 Signals](/blog/angular/angular-18-signals-new-features) menelusuri bagaimana primitif ini berkembang.
## Menjembatani Observables ke resource signal dengan rxResource
Angular 20 mempromosikan `rxResource` sebagai cara deklaratif untuk memuat data asinkron dari sebuah Observable ke dalam state berbasis signal. Ia melacak status loading, error, dan resolved sebagai signal, yang menghilangkan sebagian besar pembukuan langganan manual yang dulu dibutuhkan panggilan HTTP.
```typescript
// products.component.ts
import { Component, signal, inject } from '@angular/core';
import { rxResource } from '@angular/core/rxjs-interop';
import { HttpClient } from '@angular/common/http';
@Component({ selector: 'app-products', template: `` })
export class ProductsComponent {
private http = inject(HttpClient);
readonly category = signal('laptops');
// Angular 20 renamed the fields: request -> params and loader -> stream
readonly products = rxResource({
params: () => ({ category: this.category() }),
stream: ({ params }) =>
this.http.get(`/api/products?category=${params.category}`),
});
// products.value(), products.isLoading() and products.error() are all signals
}
```
Penggantian nama dari `request`/`loader` di Angular 19 menjadi `params`/`stream` di Angular 20 adalah kesalahan yang sering terjadi, sehingga menyebutkan nama field saat ini menunjukkan pengetahuan yang mutakhir dalam wawancara. Ketika signal `category` berubah, `rxResource` membatalkan permintaan sebelumnya dan memulai aliran baru, memberikan semantik `switchMap` secara cuma-cuma. [Referensi API rxResource](https://v20.angular.dev/api/core/rxjs-interop/rxResource) mendokumentasikan siklus hidup status secara lengkap.
## Menghindari kebocoran memori dengan takeUntilDestroyed
Setiap `subscribe()` manual yang hidup lebih lama dari komponennya membocorkan memori dan dapat memicu callback terhadap view yang sudah dihancurkan. Perbaikan historisnya adalah pola `takeUntil(this.destroy$)` dengan sebuah Subject yang diselesaikan di `ngOnDestroy`. Angular menggantikan boilerplate itu dengan `takeUntilDestroyed`, sebuah operator yang mengikat langganan ke konteks injeksi saat ini atau sebuah `DestroyRef` eksplisit.
```typescript
// live-feed.component.ts
import { Component, inject, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { interval } from 'rxjs';
@Component({ selector: 'app-live-feed', template: `` })
export class LiveFeedComponent {
private destroyRef = inject(DestroyRef);
constructor() {
interval(1000)
// completes the subscription automatically when the component is destroyed
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((tick) => console.log('tick', tick));
}
}
```
Dipanggil di dalam konstruktor atau inisialisasi field, `takeUntilDestroyed()` tidak membutuhkan argumen karena ia membaca `DestroyRef` yang ada di sekitar. Dipanggil di tempat lain, misalnya di dalam sebuah metode, ia membutuhkan `DestroyRef` eksplisit yang diteruskan, seperti ditunjukkan di atas. Mengutamakan `toSignal` dan `rxResource` menghindari langganan manual sepenuhnya, yang merupakan cara terbersih untuk mengelak dari kebocoran; ketika langganan mentah benar-benar dibutuhkan, `takeUntilDestroyed` adalah pengaman yang tepat.
> **Jebakan konteks injeksi**
>
> Memanggil `takeUntilDestroyed()` tanpa argumen di luar konteks injeksi melempar runtime error. Berlangganan di dalam event handler, sebuah `setTimeout`, atau sebuah callback RxJS berada di luar konteks itu, sehingga `DestroyRef` harus ditangkap lebih awal (biasanya sebagai field yang diinjeksi) dan diteruskan secara eksplisit. Melupakan hal ini adalah penyebab paling sering dari error "takeUntilDestroyed() can only be used within an injection context".
## Pertanyaan wawancara RxJS Angular yang layak dilatih
Pewawancara cenderung berputar di topik-topik bersinyal tinggi yang sama. Mampu menjawab ini dengan ringkas membedakan kandidat yang telah menggunakan RxJS dari mereka yang hanya membacanya.
- **Hot versus cold Observables.** Sebuah cold Observable memulai producer-nya per langganan, sehingga setiap subscriber mendapat eksekusi independen (permintaan HTTP baru). Sebuah hot Observable berbagi satu producer di seluruh subscriber, yang justru diciptakan oleh Subject dan `share()`.
- **switchMap versus mergeMap versus concatMap versus exhaustMap.** `switchMap` membatalkan Observable dalam sebelumnya (ideal untuk pencarian). `mergeMap` menjalankan semua Observable dalam secara bersamaan. `concatMap` mengantrenya dalam urutan. `exhaustMap` mengabaikan input baru selama satu sedang berjalan (ideal untuk mencegah submit formulir ganda).
- **Cara mencegah kebocoran memori.** Utamakan `toSignal`, `rxResource`, atau pipe `async`, yang semuanya berhenti berlangganan secara otomatis; untuk langganan manual, gunakan `takeUntilDestroyed`.
- **Kapan mengonversi Observable menjadi Signal.** Pilih `toSignal` ketika template membutuhkan nilai secara sinkron dan sumbernya asinkron, memungkinkan change detection membaca nilai terbaru tanpa pipe `async`.
Mengerjakan versi berjenjang dari pertanyaan-pertanyaan ini di bawah tekanan waktu adalah cara tercepat untuk menginternalisasikannya. [Pusat teknologi Angular](/technologies/angular) mengelompokkan modul RxJS, Signals, dan change detection yang memetakan langsung ke pertanyaan-pertanyaan ini.
## Kesimpulan
RxJS dan Signals adalah mitra dalam stack Angular modern, bukan rival. Poin-poin utama yang layak dibawa ke proyek atau wawancara berikutnya:
- Gunakan Signals untuk state komponen sinkron dan Observables untuk pipeline event asinkron yang membutuhkan operator.
- Pilih `switchMap` untuk membatalkan pekerjaan usang, `exhaustMap` untuk memblokir duplikat, dan pertahankan `catchError` pada Observable dalam agar aliran terluar bertahan dari kegagalan.
- Ekspos state Subject melalui `asObservable()` dan pancarkan nilai imutabel agar change detection tetap dapat diprediksi.
- Jembatani kedua model dengan `toSignal` dan `toObservable`, dan teruskan `initialValue` atau `requireSync` untuk tipe yang bersih.
- Muat data asinkron secara deklaratif dengan `rxResource` di Angular 20, dengan mengingat nama field `params` dan `stream`.
- Jaga setiap langganan manual yang tersisa dengan `takeUntilDestroyed` untuk menghilangkan kebocoran.
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/id/blog/angular/rxjs-angular-operators-subjects-signals-interop