Ücretsiz3 dakikalık yazılım sağlık testi: önce neyi düzeltmeniz gerektiğini görün
Blog

RxJS Kullanımında Yazılımcıların Yaptığı Hatalar

5 dk okuma
  • 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

İlgili yazılar