Belirli bir yan etki için ne zaman yeni bir Abonelik oluşturmalıyım?


10

Geçen hafta başka bir topluluk üyesiyle şu konu hakkında bir tartışma başlattığım bir RxJS sorusunu yanıtladım : "Belirli her yan etki için bir abonelik oluşturmalı mıyım yoksa genel olarak abonelikleri en aza indirmeli miyim?" Tam reaktif uygulama yaklaşımı veya ne zaman birinden diğerine geçilmesi için hangi yöntemin kullanılacağını bilmek istiyorum. Bu bana ve belki de başkalarına gereksiz tartışmalardan kaçınmak için yardımcı olacaktır.

Kurulum bilgileri

  • Tüm örnekler TypeScript'te
  • Soruya daha iyi odaklanmak için, abonelikler için yaşam döngüsü / kurucu kullanımı ve ilgisiz çerçevede tutma
    • Hayal edin: Abonelikler yapıcı / yaşam döngüsü başlığına eklenir
    • Hayal edin: Aboneliği iptal etmek yaşam döngüsü yok etme işleminde yapılır

Yan etki nedir (Açısal örnek)

  • Kullanıcı arayüzünde Güncelleme / Giriş (ör. value$ | async)
  • Bir bileşenin çıkışı / Akışı (ör. @Output event = event$)
  • Farklı hiyerarşilerdeki farklı hizmetler arasındaki etkileşim

Örnek kullanım alanı:

  • İki fonksiyon: foo: () => void; bar: (arg: any) => void
  • İki kaynak gözlemlenebilir: http$: Observable<any>; click$: Observable<void>
  • foohttp$yayıldıktan sonra değere ihtiyaç duyulur
  • barclick$yayıldıktan sonra çağrılır , ancak şu anki değeri gerekirhttp$

Durum: Her belirli yan etki için bir abonelik oluşturun

const foo$ = http$.pipe(
  mapTo(void 0)
);

const bar$ = http$.pipe(
  switchMap(httpValue => click$.pipe(
    mapTo(httpValue)
  )
);

foo$.subscribe(foo);
bar$.subscribe(bar);

Vaka: Genel olarak abonelikleri en aza indirin

http$.pipe(
  tap(() => foo()),
  switchMap(httpValue => click$.pipe(
    mapTo(httpValue )
  )
).subscribe(bar);

Kısacası kendi fikrim

Aboneliklerin ilk başta Rx manzaralarını daha karmaşık hale getirdiği gerçeğini anlayabiliyorum, çünkü abonelerin boruyu nasıl etkilemesi veya etkilememesi gerektiğini düşünmeniz gerekiyor (gözlemlenebilir veya paylaşılmaz). Ancak kodunuzu ne kadar çok ayırırsanız (o kadar fazla odaklanırsınız: ne zaman olur) gelecekte kodunuzu korumak (test, hata ayıklama, güncelleme) o kadar kolay olur. Bunu göz önünde bulundurarak, kodumdaki herhangi bir yan etki için her zaman tek bir gözlemlenebilir kaynak ve tek bir abonelik oluşturuyorum. Eğer sahip olduğum iki veya daha fazla yan etki aynı gözlemlenebilir kaynak tarafından tetiklenirse, gözlemlenebilir durumumu paylaşırım ve her bir yan etki için ayrı ayrı abone olurum, çünkü farklı yaşam döngüleri olabilir.

Yanıtlar:


6

RxJS, eşzamansız işlemleri yönetmek için değerli bir kaynaktır ve mümkün olduğunda kodunuzu basitleştirmek için (abonelik sayısını azaltma dahil) kullanılmalıdır. Aynı şekilde, RxJS başvurunuzdaki toplam abonelik sayısını azaltabilecek bir çözüm sunuyorsa, bir gözlemlenebilir öğeyi otomatik olarak bu gözlemlenebilir aboneliğe takip etmemelidir.

Bununla birlikte, kesinlikle 'gerekli' olmayan bir abonelik oluşturmanın faydalı olabileceği durumlar vardır:

Örnek bir istisna - gözlemlenebilirlerin tek bir şablonda yeniden kullanılması

İlk örneğinize bakın:

// Component:

this.value$ = this.store$.pipe(select(selectValue));

// Template:

<div>{{value$ | async}}</div>

$ Değeri bir şablonda yalnızca bir kez kullanılırsa, zaman uyumsuz borudan ve kod ekonomisi ve otomatik abonelikten çıkma avantajlarından faydalanırım. Bununla birlikte, bu cevaba göre , bir şablonda aynı zaman uyumsuz değişkenine yapılan birden fazla referanstan kaçınılmalıdır, örneğin:

// It works, but don't do this...

<ul *ngIf="value$ | async">
    <li *ngFor="let val of value$ | async">{{val}}</li>
</ul>

Bu durumda, bunun yerine ayrı bir abonelik oluşturmak ve bileşenimde zaman uyumsuz bir değişkeni güncellemek için bunu kullanın:

// Component

valueSub: Subscription;
value: number[];

ngOnInit() {
    this.valueSub = this.store$.pipe(select(selectValue)).subscribe(response => this.value = response);
}

ngOnDestroy() {
    this.valueSub.unsubscribe();
}

// Template

<ul *ngIf="value">
    <li *ngFor="let val of value">{{val}}</li>
</ul>

Teknik olarak, aynı sonucu olmadan elde etmek mümkündür valueSub, ancak uygulamanın gereksinimleri bunun doğru seçim olduğu anlamına gelir.

Abone olup olmayacağına karar vermeden önce gözlemlenebilir bir rolün ve ömrünün dikkate alınması

İki veya daha fazla gözlemlenebilir yalnızca birlikte alındıklarında kullanılıyorsa, bunları tek bir abonelikte birleştirmek için uygun RxJS operatörleri kullanılmalıdır.

Benzer şekilde, ilk () bir gözlemlenebilir maddenin ilk emisyonu hariç tüm filtreleri filtrelemek için kullanılıyorsa, bence kodunuzla ekonomik olmak ve 'ekstra' aboneliklerden kaçınmak için devam eden bir rolü olan bir gözlemlenebilirden daha büyük bir neden olduğunu düşünüyorum. oturum.

Her bir gözlemlenebilir özellikten herhangi birinin diğerinden bağımsız olarak faydalı olduğu durumlarda, ayrı aboneliklerin esnekliği ve netliği dikkate alınmaya değer olabilir. Ancak ilk ifademe göre, açık bir neden olmadığı sürece her gözlemlenebilir için bir abonelik otomatik olarak oluşturulmamalıdır.

Abonelik İptalleri Hakkında:

Ek aboneliklere karşı bir nokta , daha fazla abonelikten vazgeçilmesi gerektiğidir. Söylediğiniz gibi, gerekli tüm aboneliklerin Destroy'a uygulandığını varsaymak istiyoruz, ancak gerçek hayat her zaman düzgün gitmiyor! Yine, RxJS bu işlemi kolaylaştırmak için kodu basitleştiren ve bellek sızıntısı olasılığını azaltan yararlı araçlar (örn. Önce () ) sağlar. Bu makale , değerli olabilecek ilgili daha fazla bilgi ve örnek sunmaktadır.

Kişisel tercih / ayrıntı düzeyi ve terslik:

Kendi tercihlerinizi dikkate alın. Kod ayrıntılarıyla ilgili genel bir tartışmaya yönelmek istemiyorum, ancak amaç çok fazla 'gürültü' ile kodunuzu aşırı derecede şifreleme yapmak arasında doğru dengeyi bulmak olmalıdır. Bu bir göz atmaya değer olabilir .


İlk olarak ayrıntılı cevabınız için teşekkür ederiz! # 1 ile ilgili olarak: async borusu da bir abonelik / yan etki, sadece bir direktif içinde maskeleniyor. # 2 ile ilgili lütfen bazı kod örneği ekleyebilir misiniz, bana tam olarak ne söylediğinizi anlamıyorum. Abonelik İptalleri Hakkında: Aboneliklerin ngOnDestroy dışında başka bir yerde abonelikten kaldırılması gerekmeden önce hiç sahip olmadım. İlk () sizin için abonelikten çıkmayı gerçekten varsayılan olarak yönetmez: 0 emits = abonelik açık, bileşen yok olmasına rağmen.
Jonathan Stellwag

1
2. nokta gerçekten de bir aboneliğin her bir gözlemlenebilirliğini otomatik olarak takip etmek yerine, bir abonelik ayarlayıp kurmamaya karar verirken her bir gözlemlenebilir rolün dikkate alınmasıyla ilgilidir. Bu noktada, medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87 'ye bakmanızı öneririm. Rx'in gücünün
Matt Saunders

1
@JonathanStellwag teşekkür ederiz. Bilgi RE geri çağrıları için de teşekkürler - uygulamalarınızda, bunu önlemek için her aboneliğin (örneğin ilk () bile kullanıldığında) açıkça aboneliğinizi iptal ediyor musunuz?
Matt Saunders

1
Evet ediyorum. Mevcut projede, her zaman abonelikten çıkarak takılan tüm düğümlerin yaklaşık% 30'unu en aza indirdik.
Jonathan Stellwag

2
Abonelikten çıkma tercihim takeUntil, çağrılan bir işlevle birlikte ngOnDestroy. Bu boruya bu ekler bir astar var: takeUntil(componentDestroyed(this)). stackoverflow.com/a/60223749/5367916
Kurt Hamilton

2

abonelikleri optimize etmek son oyununuzsa, neden mantıksal uç noktaya gitmiyorsunuz ve sadece bu genel kalıbı takip etmiyorsunuz:

 const obs1$ = src1$.pipe(tap(effect1))
 const obs2$ = src2$pipe(tap(effect2))
 merge(obs1$, obs2$).subscribe()

Muslukta sadece yan etkileri yürütmek ve birleştirme ile etkinleştirmek, yalnızca bir aboneliğiniz olduğu anlamına gelir.

Bunu yapmamanın bir nedeni, RxJS'yi kullanışlı kılan şeylerin çoğunu kısırlaştırmanızdır. Bu, gözlemlenebilir akışları oluşturma ve gerektiğinde akışlardan abone olma / abonelikten çıkma yeteneğidir.

Gözlenebilirlerinizin mantıksal olarak oluşturulması ve abonelikleri azaltmak adına kirletilmemesi veya karıştırılmaması gerektiğini savunuyorum. Foo efekti mantıksal olarak çubuk efekti ile eşleştirilmeli mi? Biri diğerini gerektiriyor mu? Http $ yaydığında tetikleyici foo istemeyecek miyim? İlgisiz işlevler arasında gereksiz bağlantı oluşturuyor muyum? Bunların hepsi, onları tek bir akışa yerleştirmekten kaçınmak için nedenlerdir.

Bunların hepsi, birden fazla abonelik IMO ile yönetilmesi daha kolay olan hata işlemeyi düşünmüyor bile


Cevabınız için teşekkür ederim. Sadece bir cevabı kabul edebildiğim için üzgünüm. Cevabınız @Matt Saunders tarafından yazılanla aynı haktır. Bu sadece başka bir bakış açısı. Matt'in çabası yüzünden ona kabul ettim. Umarım beni affedebilirsin :)
Jonathan Stellwag
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.