ฉันควรสร้างการสมัครสมาชิกใหม่สำหรับผลข้างเคียงที่เฉพาะเจาะจงเมื่อใด


10

สัปดาห์ที่แล้วฉันตอบคำถาม RxJS ที่ฉันได้พูดคุยกับสมาชิกชุมชนคนอื่นเกี่ยวกับ: "ฉันควรสร้างการสมัครรับข้อมูลสำหรับผลข้างเคียงที่เฉพาะเจาะจงทุกอย่างหรือฉันควรพยายามลดการสมัครสมาชิกให้น้อยที่สุด?" ฉันต้องการทราบว่าจะใช้ methology ใดในแง่ของวิธีการแอปพลิเคชั่นที่มีปฏิกิริยาเต็มรูปแบบหรือเมื่อใดที่จะเปลี่ยนจากที่หนึ่งไปยังอีกที่หนึ่ง สิ่งนี้จะช่วยฉันและคนอื่น ๆ ในการหลีกเลี่ยงการอภิปรายที่ไม่จำเป็น

ข้อมูลการตั้งค่า

  • ตัวอย่างทั้งหมดอยู่ใน TypeScript
  • เพื่อมุ่งเน้นคำถามที่ดีกว่าไม่มีการใช้วงจรชีวิต / ตัวสร้างสำหรับการสมัครสมาชิกและเพื่อให้อยู่ในกรอบที่ไม่เกี่ยวข้อง
    • ลองนึกภาพ: การสมัครสมาชิกถูกเพิ่มใน constructor / lifecycle init
    • ลองนึกภาพ: การยกเลิกการสมัครสมาชิกทำในช่วงเวลาทำลายล้าง

ผลข้างเคียงคืออะไร (ตัวอย่างเชิงมุม)

  • อัปเดต / อินพุตใน UI (เช่นvalue$ | async)
  • เอาท์พุท / ต้นน้ำขององค์ประกอบ (เช่น@Output event = event$)
  • การโต้ตอบระหว่างบริการต่าง ๆ ในลำดับชั้นที่แตกต่างกัน

ตัวอย่างที่เป็นประโยชน์:

  • สองฟังก์ชั่น: foo: () => void; bar: (arg: any) => void
  • แหล่งข้อมูลที่สังเกตได้สองแหล่ง: http$: Observable<any>; click$: Observable<void>
  • fooถูกเรียกหลังจากhttp$ปล่อยออกมาและไม่ต้องการค่าใด ๆ
  • barถูกเรียกหลังจากclick$ปล่อยออกมา แต่ต้องการค่าปัจจุบันของhttp$

กรณี: สร้างการสมัครรับข้อมูลสำหรับผลข้างเคียงที่เฉพาะเจาะจงทุกรายการ

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

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

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

กรณี: ลดการสมัครรับข้อมูลให้น้อยที่สุดโดยทั่วไป

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

ความเห็นของฉันเองในระยะสั้น

ฉันสามารถเข้าใจความจริงที่ว่าการสมัครสมาชิกทำให้ภูมิทัศน์ Rx ซับซ้อนขึ้นในตอนแรกเพราะคุณต้องคิดว่าสมาชิกควรมีผลต่อไพพ์หรือไม่เช่นนั้น (แชร์ให้คุณสังเกตได้หรือไม่) แต่ยิ่งคุณแยกรหัสของคุณออกมามากเท่าใด (ยิ่งคุณเน้นมากขึ้น: เกิดอะไรขึ้นเมื่อ) ยิ่งรักษารหัสของคุณได้ง่ายขึ้นในอนาคต โดยที่ในใจฉันมักจะสร้างแหล่งที่สังเกตได้เดียวและการสมัครสมาชิกเดียวสำหรับผลข้างเคียงใด ๆ ในรหัสของฉัน หากมีผลข้างเคียงสองอย่างขึ้นไปที่ฉันถูกกระตุ้นจากแหล่งข้อมูลเดียวกันที่สังเกตได้ฉันจะแบ่งปันสิ่งที่สังเกตได้และสมัครสมาชิกผลข้างเคียงแต่ละอันเพราะมันสามารถมีวงจรชีวิตที่แตกต่างกัน

คำตอบ:


6

RxJS เป็นทรัพยากรที่มีค่าสำหรับการจัดการการดำเนินการแบบอะซิงโครนัสและควรใช้เพื่อทำให้รหัสของคุณง่ายขึ้น (รวมถึงการลดจำนวนการสมัครสมาชิก) ที่เป็นไปได้ อย่างเท่าเทียมกันผู้สังเกตการณ์ไม่ควรตามด้วยการสมัครรับข้อมูลที่สังเกตได้โดยอัตโนมัติหาก RxJS มีวิธีแก้ปัญหาซึ่งสามารถลดจำนวนการสมัครสมาชิกโดยรวมในแอปพลิเคชันของคุณ

อย่างไรก็ตามมีสถานการณ์ที่อาจเป็นประโยชน์ในการสร้างการสมัครรับข้อมูลที่ไม่จำเป็นอย่างเคร่งครัด:

ข้อยกเว้นตัวอย่าง - ใช้ซ้ำของ observables ในแม่แบบเดียว

ดูตัวอย่างแรกของคุณ:

// Component:

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

// Template:

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

ถ้า value $ ถูกใช้เพียงครั้งเดียวในเทมเพลตฉันจะใช้ประโยชน์จากไพพ์ async และผลประโยชน์สำหรับการประหยัดโค้ดและการยกเลิกการสมัครอัตโนมัติ อย่างไรก็ตามตามคำตอบนี้ควรหลีกเลี่ยงการอ้างอิงหลายตัวแปร async เดียวกันในแม่แบบเช่น:

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

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

ในสถานการณ์นี้ฉันจะสร้างการสมัครสมาชิกแยกต่างหากและใช้สิ่งนี้เพื่ออัปเดตตัวแปรที่ไม่ใช่ async ในองค์ประกอบของฉัน:

// 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>

ในทางเทคนิคเป็นไปได้ที่จะได้ผลลัพธ์เดียวกันโดยไม่ต้องvalueSubมี แต่ข้อกำหนดของแอปพลิเคชันหมายความว่านี่เป็นตัวเลือกที่เหมาะสม

พิจารณาบทบาทและอายุการใช้งานของสิ่งที่สังเกตได้ก่อนตัดสินใจว่าจะสมัครสมาชิกหรือไม่

หากมีการใช้งานสองอย่างหรือมากกว่านั้นเมื่อใช้ร่วมกันควรใช้ตัวดำเนินการ RxJS ที่เหมาะสมเพื่อรวมเข้ากับการสมัครสมาชิกเดียว

ในทำนองเดียวกันถ้าfirst ()ถูกใช้เพื่อกรองออกทั้งหมดยกเว้นการปล่อยครั้งแรกของสิ่งที่สังเกตได้ฉันคิดว่ามีเหตุผลมากกว่าที่จะประหยัดด้วยรหัสของคุณและหลีกเลี่ยงการสมัครสมาชิก 'พิเศษ' มากกว่าสำหรับสิ่งที่สังเกตได้ซึ่งมีบทบาทอย่างต่อเนื่องใน เซสชั่น

ในกรณีที่ผู้สังเกตการณ์รายใดรายหนึ่งมีประโยชน์อย่างเป็นอิสระจากผู้อื่นความยืดหยุ่นและความชัดเจนของการสมัครสมาชิกแยกต่างหากอาจคุ้มค่าที่จะพิจารณา แต่ตามคำสั่งเริ่มต้นของฉันการสมัครสมาชิกไม่ควรถูกสร้างขึ้นโดยอัตโนมัติสำหรับทุกคนที่สามารถสังเกตเห็นได้เว้นแต่จะมีเหตุผลที่ชัดเจนที่จะทำเช่นนั้น

เกี่ยวกับการยกเลิกการสมัคร:

จุดต่อการสมัครสมาชิกเพิ่มเติมคือต้องยกเลิกการสมัครสมาชิกเพิ่มเติม ดังที่คุณได้กล่าวไปแล้วเราขอสมมติให้มีการยกเลิกการสมัครสมาชิกที่จำเป็นทั้งหมดบนเดสรอย แต่ชีวิตจริงไม่ได้เป็นไปอย่างราบรื่น! อีกครั้ง RxJS ให้เครื่องมือที่มีประโยชน์ (เช่นแรก () ) เพื่อปรับปรุงกระบวนการนี้ซึ่งลดความซับซ้อนของรหัสและลดโอกาสในการรั่วไหลของหน่วยความจำ บทความนี้ให้ข้อมูลเพิ่มเติมและตัวอย่างที่เกี่ยวข้องซึ่งอาจมีค่า

การตั้งค่าส่วนตัว / verbosity vs. terseness:

พิจารณาการตั้งค่าของคุณเอง ฉันไม่ต้องการที่จะหลงทางไปสู่การสนทนาทั่วไปเกี่ยวกับการใช้คำฟุ่มเฟื่อยของรหัส แต่เป้าหมายควรจะหาสมดุลที่เหมาะสมระหว่าง 'เสียง' มากเกินไปและทำให้รหัสของคุณมีความลับสุดเหวี่ยงนี้อาจจะคุ้มค่าดู


ก่อนอื่นขอบคุณสำหรับคำตอบโดยละเอียดของคุณ! เกี่ยวกับ # 1: จากมุมมองของฉันท่อ async ยังเป็นสมาชิก / ผลข้างเคียงเพียงว่ามันถูกหลอกลวงภายในคำสั่ง เกี่ยวกับ # 2 คุณสามารถเพิ่มตัวอย่างรหัสได้ไหมฉันไม่เข้าใจเลยว่าคุณบอกฉันตรงไหน เกี่ยวกับการยกเลิกการสมัคร: ฉันไม่เคยมีมาก่อนการสมัครสมาชิกนั้นจะต้องยกเลิกการสมัครที่อื่นนอกเหนือจาก ngOnDestroy ขั้นแรก () ไม่ได้จัดการการยกเลิกการสมัครสำหรับคุณโดยค่าเริ่มต้น: 0 emits = การสมัครสมาชิกเปิดแม้ว่าองค์ประกอบจะถูกทำลาย
Jonathan Stellwag

1
จุดที่ 2 เป็นเพียงแค่การพิจารณาบทบาทของแต่ละสิ่งที่สังเกตได้เมื่อตัดสินใจว่าจะตั้งค่าการสมัครรับข้อมูลด้วยหรือไม่แทนที่จะติดตามทุกการติดตามด้วยการสมัครรับข้อมูล ในจุดนี้ฉันขอแนะนำให้ดูที่medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87ซึ่งกล่าวว่า: "การรักษาวัตถุการสมัครรับข้อมูลมากเกินไปนั้นเป็นสัญญาณที่คุณจัดการกับการสมัครสมาชิกของคุณอย่างไม่มีความหมายและไม่ได้ใช้ประโยชน์ จากพลังของ Rx "
แมตต์แซนเดอร์

1
ขอบคุณมากขอบคุณ @JonathanStellwag ขอขอบคุณสำหรับข้อมูลการเรียกกลับ RE - ในแอปของคุณคุณได้ยกเลิกการสมัครทุกการสมัครอย่างชัดเจน (แม้ในกรณีที่มีการใช้ครั้งแรก () เพื่อป้องกัน)
แมตต์แซนเดอร์

1
ใช่ฉันทำ. ในโครงการปัจจุบันเราย่อเล็กสุดประมาณ 30% ของโหนดทั้งหมดที่แขวนอยู่รอบ ๆ เพียงแค่ยกเลิกการเป็นสมาชิกเสมอ
Jonathan Stellwag

2
การตั้งค่าของฉันสำหรับการยกเลิกการสมัครtakeUntilร่วมกับฟังก์ชันที่เรียกngOnDestroyใช้ takeUntil(componentDestroyed(this))มันเป็นหนึ่งซับที่เพิ่มนี้ไปยังท่อ: stackoverflow.com/a/60223749/5367916
Kurt Hamilton

2

หากการเพิ่มประสิทธิภาพการสมัครรับข้อมูลเป็น endgame ของคุณทำไมไม่ไปที่ตรรกะสุดขีดและเพียงทำตามรูปแบบทั่วไปนี้:

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

ดำเนินการผลข้างเคียงโดยเฉพาะในการแตะและเปิดใช้งานด้วยการผสานหมายความว่าคุณมีการสมัครเพียงครั้งเดียวเท่านั้น

เหตุผลหนึ่งที่ไม่ควรทำเช่นนี้คือคุณกำลังทำหมันสิ่งที่ทำให้ RxJS มีประโยชน์ ซึ่งเป็นความสามารถในการเขียนสตรีมที่สังเกตได้และสมัคร / ยกเลิกการสมัครจากสตรีมตามที่ต้องการ

ฉันจะยืนยันว่าสิ่งที่ควรสังเกตของคุณควรมีเหตุผลและไม่ทำให้เสียหรือสับสนในชื่อของการลดการสมัครสมาชิก เอฟเฟกต์ foo ควรมีเหตุผลควบคู่ไปกับเอฟเฟกต์บาร์หรือไม่ คนเราต้องการสิ่งอื่นหรือไม่? ฉันอาจจะไม่ต้องการทริกเกอร์ foo เมื่อ http $ ปล่อยออกมาหรือไม่? ฉันกำลังสร้างการเชื่อมต่อที่ไม่จำเป็นระหว่างฟังก์ชั่นที่ไม่เกี่ยวข้องหรือไม่? นี่คือเหตุผลทั้งหมดเพื่อหลีกเลี่ยงการวางไว้ในสตรีมเดียว

ทั้งหมดนี้ไม่ได้พิจารณาถึงการจัดการข้อผิดพลาดซึ่งง่ายต่อการจัดการด้วยการสมัคร IMO หลายรายการ


ขอบคุณสำหรับคำตอบ. ฉันขอโทษที่ฉันสามารถยอมรับคำตอบเดียวเท่านั้น คำตอบของคุณมีสิทธิ์เช่นเดียวกับที่เขียนโดย @Matt Saunders มันเป็นอีกมุมมองหนึ่ง เนื่องจากความพยายามของแมตต์ฉันจึงยอมรับเขา ฉันหวังว่าคุณจะยกโทษให้ฉัน :)
Jonathan Stellwag
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.