RxJS yang ditanyakan di interview Angular
Observable vs Promise, Subject/BehaviorSubject, operator inti, dan cara mencegah memory leak
switchMap, dan bagaimana Anda mencegah memory leak di Angular?"
dalam 60 detik, lalu memilih operator yang tepat untuk skenario search box, request paralel, dan tombol Simpan.
1. Observable vs Promise
Anda sudah kenal Promise dari fetch(). RxJS menyebut Observable sebagai
"lazy Push collections of multiple values"
[1]:
sama-sama push seperti Promise, tapi lazy dan bisa berisi banyak nilai.
Dokumentasi Angular v16 merangkum bedanya [2]:
| Promise (yang Anda kenal) | Observable | |
|---|---|---|
| Kapan jalan | "Promises execute immediately on creation." | "computation does not start until subscription." |
| Jumlah nilai | "Promises provide one." | "Observables provide many values." |
| Batal | "Promises are not cancellable." | "Observable subscriptions are cancellable." |
| Chaining | .then() / .catch() | .pipe(operator...) lalu .subscribe() |
Akibat praktisnya di Angular: HttpClient mengembalikan Observable, dan
"the method doesn't begin its HTTP request until you call subscribe()".
Sebaliknya, "Subscribing twice results in two HTTP requests"
[3].
Lupa subscribe berarti tidak ada request sama sekali, sesuatu yang tidak pernah terjadi dengan fetch.
// React + fetch: langsung jalan
useEffect(() => { fetch('/api/products').then(r => r.json()).then(setProducts); }, []);
// Angular: baru jalan saat subscribe
this.http.get<Product[]>('/api/products')
.pipe(map(list => list.filter(p => p.stock > 0))) // seperti .then(transform)
.subscribe(list => (this.products = list)); // seperti .then(setProducts)
2. Subject dan BehaviorSubject: state bersama di service
"An RxJS Subject is a special type of Observable that allows values to be multicasted to many Observers".
"Every Subject is an Observable" dan "Every Subject is an Observer", jadi Anda bisa subscribe sekaligus memanggil next(v).
BehaviorSubject "has a notion of 'the current value'": ia "stores the latest value emitted to its consumers"
dan langsung mengirimkannya ke subscriber baru
[4].
Inilah pola Angular Service untuk state bersama (padanan React: state di Context Provider, atau store Zustand):
@Injectable({ providedIn: 'root' })
export class CartService {
private readonly items = new BehaviorSubject<Product[]>([]); // wajib nilai awal
readonly items$ = this.items.asObservable(); // luar hanya bisa subscribe
add(p: Product) { this.items.next([...this.items.value, p]); } // seperti setState
}
Pakai Subject biasa untuk event (subscriber baru tidak butuh nilai lama), BehaviorSubject untuk state.
ReplaySubject mengulang beberapa nilai lama ke subscriber baru [4].
3. Operator inti: empat yang hampir pasti ditanyakan
"Operators are functions" yang dirangkai di dalam pipe()
[5].
map sama seperti Array.map, tapi untuk setiap nilai yang lewat. Yang membedakan kandidat adalah keluarga flattening:
operator yang memetakan setiap nilai ke Observable baru (biasanya request HTTP).
| Operator | Saat nilai baru datang dan request lama belum selesai… | Skenario khas |
|---|---|---|
switchMap | "stops emitting items from the earlier-emitted inner Observable and begins emitting items from the new one" [6] (request lama dibatalkan) | Search box, route param berubah |
mergeMap | Semua jalan bersamaan; parameter concurrent = "Maximum number of ObservableInputs being subscribed to concurrently" [7] | Hapus/upload massal, urutan tidak penting |
concatMap | Mengantre: "equivalent to mergeMap with concurrency parameter set to 1" [8] | Simpan berurutan, urutan penting |
exhaustMap | "ignores every new projected Observable if the previous projected Observable has not yet completed" [9] | Tombol Simpan / Login yang diklik berkali-kali |
Tiga operator lain yang melengkapi jawaban:
debounceTime(ms): "Emits a notification from the source Observable only after a particular time span has passed without another source emission" [10]. Padanan React: hookuseDebounce.catchError: "Catches errors on the observable to be handled by returning a new observable or throwing an error" [11]. Padanan:.catch()pada Promise.forkJoin: "Wait for Observables to complete and then combine last values they emitted" [12]. Padanan:Promise.all. Kalau satu error,forkJoinikut error.
Search box di dokumentasi Angular v16 memakai rangkaian ini [13] (disesuaikan ke Product Catalog):
this.results$ = this.searchControl.valueChanges.pipe(
filter(text => text.length > 2),
debounceTime(300), // tunggu user berhenti mengetik
distinctUntilChanged(), // abaikan kata kunci yang sama
switchMap(term => this.products.search(term).pipe(
catchError(() => of([])) // satu request gagal tidak mematikan stream
)),
);
Kenapa catchError ditaruh di dalam switchMap? Kalau error sampai ke stream luar, stream itu selesai dan search box berhenti bekerja.
4. Mencegah memory leak: async pipe dulu, takeUntil kalau perlu
Lesson 0013 sudah mengutip aturan Angular di ngOnDestroy: "Unsubscribe Observables and detach event handlers to avoid memory leaks"
[14].
Subscription manual ke stream yang tidak pernah selesai (interval, valueChanges, BehaviorSubject di service) tetap hidup setelah component dihancurkan.
Padanan React: useEffect tanpa fungsi cleanup.
-
asyncpipe (pilihan utama). Ia "subscribes to anObservableorPromiseand returns the latest value it has emitted", dan "When the component gets destroyed, theasyncpipe unsubscribes automatically to avoid potential memory leaks" [15].<li *ngFor="let p of results$ | async">{{ p.name }}</li> -
takeUntil(kalau harus subscribe di class). Ia meneruskan nilai "until anotifierObservable emits a value", lalu complete [16].private readonly destroy$ = new Subject<void>(); ngOnInit() { this.cart.items$.pipe(takeUntil(this.destroy$)) // taruh di akhir pipe .subscribe(items => (this.count = items.length)); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }
Di v16 juga ada takeUntilDestroyed() dari @angular/core/rxjs-interop, "Operator which completes the Observable when the calling context (component, directive, service, etc) is destroyed",
tapi statusnya masih developer preview [17].
Sebut sebagai alternatif baru; jawab dengan async pipe dan takeUntil dulu.
Latihan 1 — Kuis konsep
Jawab tanpa scroll ke atas; kalau ragu, tebak dulu, lalu baca penjelasannya.
Kuis di lesson ini adalah feedback cepat, bukan bukti penguasaan; yang dicatat hanya hasil di mock interview 0018.
Latihan 2 — Pilih operatornya
Setiap soal adalah skenario dari Product Catalog. Sebelum mengklik, ucapkan alasannya dalam satu kalimat: "karena request lama harus…".
Latihan 3 — Ucapkan dengan suara keras
Pasang timer 60 detik. Jawab dengan suara keras, seolah sedang interview. Jangan buka jawaban contoh sebelum selesai.
"Apa beda Observable dan Promise, kapan Anda memakai switchMap, dan bagaimana Anda mencegah memory leak di Angular?"
Bandingkan dengan jawaban contoh dan checklist-nya
"Promise langsung jalan saat dibuat, hanya menghasilkan satu nilai, dan tidak bisa dibatalkan.
Observable lazy, baru jalan saat subscribe, bisa memancarkan banyak nilai, dan bisa dibatalkan dengan unsubscribe.
HttpClient mengembalikan Observable. Untuk state bersama saya pakai BehaviorSubject di service,
karena subscriber baru langsung dapat nilai terakhir. Operator saya rangkai di pipe: map untuk transformasi, catchError untuk error.
Di search box saya pakai debounceTime lalu switchMap, karena switchMap membatalkan request lama
sehingga hasil lama tidak menimpa yang baru. Kalau semua request harus jalan paralel saya pakai mergeMap,
dan forkJoin untuk menunggu beberapa request sekaligus, seperti Promise.all.
Untuk mencegah memory leak saya utamakan async pipe yang unsubscribe otomatis.
Kalau harus subscribe manual, saya pakai takeUntil(destroy$) yang di-next() di ngOnDestroy."
Checklist: apakah Anda menyebut…
- Observable lazy dan bisa banyak nilai, sedangkan Promise eager dan satu nilai?
- Observable bisa dibatalkan (
unsubscribe), Promise tidak? BehaviorSubjectuntuk state bersama karena menyimpan nilai terakhir?switchMapmembatalkan request lama (search box), bersamadebounceTime?- beda
switchMapdenganmergeMap(paralel) atauforkJoin(tunggu semua)? asyncpipe atautakeUntil+ngOnDestroyuntuk mencegah memory leak?
Kalau kurang dari 4 poin, ulangi sebelum lesson 0016. Soal ini juga akan muncul lagi di mock interview 0018.
Ringkasan untuk dicetak: Reference: RxJS untuk interview Angular 16 · Istilah: GLOSSARY.md
Ada yang belum jelas? Tanyakan ke saya (agent) di terminal, misalnya: "apakah subscription ke HttpClient juga wajib di-unsubscribe?" atau "kapan saya pakai combineLatest dan bukan forkJoin?"