RxJS Kullanımında Yazılımcıların Yaptığı Hatalar
- RxJS
- Angular
- TypeScript
İçindekiler
RxJS güçlü bir araç, ama yanlış kullanıldığında bakımı zor, sızıntılı ve sunucuyu gereksiz isteklerle dolduran kodlara yol açıyor. Angular projelerinde en sık karşılaştığım altı hatayı, her birinin doğru karşılığıyla birlikte topladım.
1. İç içe abonelik
Reaktif programlamaya yeni başlayanların en sık yaptığı hata. Bir akışın sonucunu diğerine aktarmak için subscribe içinde subscribe yazılır. Sonuç: iptal edilemeyen istekler, sıralama hataları ve okunamayan kod.
// Yanlış
this.route.paramMap.subscribe((params) => {
this.userService.get(params.get('id')!).subscribe((user) => {
this.user = user;
});
});
// Doğru: akışlar operatörle birleştirilir, tek abonelik kalır
this.user$ = this.route.paramMap.pipe(
switchMap((params) => this.userService.get(params.get('id')!)),
);switchMap yeni bir parametre geldiğinde önceki isteği iptal eder; kullanıcı hızlıca sayfa değiştirdiğinde eski cevabın yenisinin üzerine yazılması da böylece önlenir.
2. Akışları çalışma anında yeniden oluşturmak
Imperative programlamaya alışkın geliştiriciler, her değişiklikte yeni bir Observable oluşturup eskisinin yerine koyar. Bu, takibi ve hata ayıklaması zor bir yapı ortaya çıkarır. Bunun yerine değişen değeri bir akış olarak tanımlayın ve sonucu ondan türetin:
// Yanlış: her aramada akış baştan kuruluyor
onSearch(term: string) {
this.results$ = this.api.search(term);
}
// Doğru: arama terimi bir akış, sonuçlar ondan türetiliyor
private readonly term$ = new Subject<string>();
readonly results$ = this.term$.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap((term) => this.api.search(term)),
);
onSearch(term: string) {
this.term$.next(term);
}3. Akışı paylaşmamak
HTTP Observable’ları “soğuk”tur: her abonelik isteği yeniden gönderir. Aynı veriyi şablonda iki yerde async pipe ile kullanmak, sunucuya iki istek atmak demektir. Yüzlerce kullanıcıda bu, gereksiz yükün katlanarak artmasına yol açar.
readonly settings$ = this.http.get<Settings>('/api/settings').pipe(
// Tek istek atılır, son değer sonraki abonelere de verilir
shareReplay({ bufferSize: 1, refCount: true }),
);4. Abonelikleri temizlememek
Bileşen yok edildiğinde sonlanmayan abonelikler çalışmaya devam eder. interval, WebSocket ya da store akışları gibi bitmeyen akışlarda bu bellek sızıntısına ve aynı işin defalarca yapılmasına yol açar. Mümkünse async pipe kullanın; abonelik bileşenle birlikte kendiliğinden kapanır. Kodda abone olmanız gerekiyorsa:
export class DashboardComponent {
constructor(private readonly live: LiveService) {
this.live.updates$
.pipe(takeUntilDestroyed()) // Angular 16+
.subscribe((update) => this.apply(update));
}
}5. Hataları yanlış yerde yakalamak
Bir akış hata verdiğinde tamamlanır ve bir daha değer üretmez. catchError dış akışa konursa ilk hatadan sonra arama kutusu tamamen çalışmaz hale gelir. Hatayı iç akışta yakalayın ki dış akış yaşamaya devam etsin:
readonly results$ = this.term$.pipe(
switchMap((term) =>
this.api.search(term).pipe(
// Yalnızca bu istek başarısız olur; sonraki aramalar çalışır
catchError(() => of([])),
),
),
);6. Yanlış operatör seçimi
“Alet işler, el övünür” sözü RxJS operatörleri için de geçerli. Dört eşleme operatörü aynı işi yapıyor gibi görünür ama farklı sonuçlar üretir; yanlışını seçmek en kötü ihtimalle arka uçta tutarsız veriye yol açar:
switchMap: yeni değer gelince öncekini iptal eder. Arama, filtreleme, sayfa parametreleri.concatMap: sırayla, biri bitince diğerini çalıştırır. Sırası önemli kayıt işlemleri.mergeMap: hepsini paralel çalıştırır. Birbirinden bağımsız işler.exhaustMap: biri sürerken gelenleri yok sayar. Çift tıklamaya karşı form gönderimi, giriş butonu.
Örneğin bir kaydetme butonunda switchMap kullanmak, kullanıcı iki kez tıkladığında ilk isteği istemci tarafında iptal eder ama sunucuda işlem çoktan başlamış olabilir. Doğrusu exhaustMap ya da concatMap’tir.
Güncelleme notu
Bu yazının ilk hali 2023’te Medium’da yayımlandı ve yalnızca hataların başlıklarını içeriyordu. Burada her birine yanlış ve doğru kullanımı gösteren örnekler ekledim.
Yeni yazılar e-postanıza gelsin
Yeni bir yazı yayımladığımda kısa bir e-posta alırsınız. İstediğiniz zaman tek tıkla çıkabilirsiniz. Gizlilik