Subject vs BehaviorSubject vs ReplaySubject ใน Angular


124

ฉันต้องการทำความเข้าใจ 3 สิ่งเหล่านี้:

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

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

ฉันต้องการคำอธิบายที่ชัดเจนไม่ใช่แค่ "a + b => c ที่คุณสมัครรับข้อมูล .... "

ขอบคุณ


1
มีคำถามเกี่ยวกับพฤติกรรมที่สังเกตได้อยู่แล้ว stackoverflow.com/questions/39494058/…และเอกสารประกอบเรื่อง replay ก็ชัดเจน imo github.com/Reactive-Extensions/RxJS/blob/master/doc/api/…
eko

คำตอบนี้มีการนำเสนออย่างละเอียดถี่ถ้วนเกี่ยวกับหัวข้อใน Rxjs ซึ่งช่วยเติมเต็มคำตอบจาก peeksilet ซึ่งรวมถึงรายละเอียดที่สำคัญเกี่ยวกับพฤติกรรมหลังการเลิกจ้างด้วยดังนั้นจึงควรพิจารณาดู
user3743222

คำตอบ:


278

มันขึ้นอยู่กับพฤติกรรมและความหมายจริงๆ กับ

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

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

    BehaviorSubjectจะเริ่มต้นด้วยค่าเริ่มต้น บางครั้งสิ่งนี้สำคัญต่อการตั้งค่าการเข้ารหัส ตัวอย่างเช่นคุณเริ่มต้นด้วยไฟล์null. จากนั้นในการสมัครของคุณคุณต้องทำการตรวจสอบค่าว่าง อาจจะโอเคหรืออาจจะน่ารำคาญ

  • ReplaySubject- สามารถแคชการปล่อยมลพิษได้ถึงจำนวนที่ระบุ สมาชิกทุกคนจะได้รับค่าแคชทั้งหมดเมื่อสมัครสมาชิก คุณต้องการพฤติกรรมนี้เมื่อใด ตามจริงแล้วฉันไม่ได้มีความจำเป็นใด ๆ สำหรับพฤติกรรมดังกล่าวยกเว้นในกรณีต่อไปนี้:

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

ดังนั้นมันจึงขึ้นอยู่กับพฤติกรรมที่คุณคาดหวัง (ว่าจะใช้แบบไหน) เวลาส่วนใหญ่คุณอาจต้องการใช้ a BehaviorSubjectเพราะสิ่งที่คุณต้องการเป็นตัวแทนจริงๆก็คือความหมาย "ตามช่วงเวลา" นั่นเอง แต่โดยส่วนตัวแล้วฉันไม่เห็นอะไรผิดปกติกับการเปลี่ยนReplaySubjectค่าเริ่มต้นด้วย1.

สิ่งที่คุณต้องการหลีกเลี่ยงคือการใช้วานิลลาSubjectเมื่อสิ่งที่คุณต้องการจริงๆคือพฤติกรรมการแคช ยกตัวอย่างเช่นคุณกำลังเขียน Routing Guard หรือแก้ปัญหา Subjectคุณดึงข้อมูลบางอย่างในยามที่ตั้งไว้ในการให้บริการ จากนั้นในองค์ประกอบที่กำหนดเส้นทางคุณสมัครรับบริการตามเป้าหมายเพื่อพยายามรับค่าที่ถูกปล่อยออกมาในยาม อุ่ย ค่าอยู่ที่ไหน? มันถูกปล่อยออกมาแล้ว DUH ใช้หัวเรื่อง "แคช"!

ดูสิ่งนี้ด้วย:


1
นี่คือความแตกต่างที่สั้นและเข้าใจง่าย เมื่อค่าเปลี่ยนไปในบริการและส่วนประกอบเปลี่ยนไปด้วยเช่นกันที่แสดงค่า BehaviourSubjects หรือ Replay Subject เป็นวิธีแก้ปัญหา
Saiyaff Farouk

1
ขอบคุณ! ReplaySubjectด้วยขนาดบัฟเฟอร์ 1 คือสิ่งที่ฉันต้องการ ฉันมีตัวป้องกันเส้นทางที่ต้องการค่านี้ แต่ต้องรอให้มีการปล่อยครั้งแรก ดังนั้น a BehaviorSubjectไม่ได้ตัดมันเนื่องจากฉันไม่ต้องการค่าเริ่มต้น (ใช้nullไม่ได้เช่นกันเพราะฉันใช้มันเพื่อบ่งบอกสถานะ)
menehune23

1
@ menehune23 ฉันต้องการ ReplaySubject สำหรับresolveคลาสAngular guard ด้วย บริการข้อมูลของฉันอาจเป็นแบบอะซิงโครนัสหรือซิงโครนัส (หากดึงข้อมูลไปแล้ว) หากเป็นแบบซิงโครนัส Subject.next () จะถูกยิงก่อนไฟล์resolveทำงานฟังก์ชันจะกลับมาและถูกสมัครสมาชิกโดย Angular ภายใน BehaviourSubject อาจใช้งานได้ แต่ฉันจะต้องโทรอย่างชัดเจนcomplete()และเพิ่มการnullตรวจสอบสำหรับค่าเริ่มต้น สิ่งที่ได้ผลเป็นสิ่งใหม่ReplaySubject<DataType>(1) และresolveSubject.asObservable().take(1).map(....)
Drenai

1
ฉันใช้ ReplaySubject ที่มีขนาดบัฟเฟอร์เท่ากับ 1 แต่ด้วยเหตุผลบางประการเมื่อฉันได้รับ Observable ด้วย .asObservable() Observable จะส่งมูลค่าnullให้กับสมาชิกก่อนที่ฉันจะโทรหาnext()ReplaySubject ของฉัน ฉันคิดว่ามันไม่ควรมีค่าเริ่มต้นที่แตกต่างจาก BehaviorSubject?
Kyle V.

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

16

ผลประโยชน์ของประเภทที่สังเกตที่แตกต่างกัน, การตั้งชื่อที่ใช้งานง่ายไม่ใช่ฉันรู้นะครับ

  • Subject - สมาชิกจะได้รับค่าที่เผยแพร่หลังจากสมัครสมาชิกแล้วเท่านั้น
  • BehaviorSubject - สมาชิกใหม่จะได้รับมูลค่าที่เผยแพร่ล่าสุดหรือมูลค่าเริ่มต้นทันทีที่สมัครสมาชิก
  • ReplaySubject - สมาชิกใหม่จะได้รับมูลค่าที่เผยแพร่ก่อนหน้านี้ทั้งหมดทันทีที่สมัครสมาชิก

1-n ค่าที่เผยแพร่? ดังนั้นหากมีค่าที่เผยแพร่ 2 ค่า ReplaySubject จะสร้าง -1 ค่าที่เผยแพร่ ???
Jason Cheng

@JasonCheng ไม่ดึงค่าที่เผยแพร่ก่อนหน้านี้ทั้งหมดเมื่อสมัครสมาชิกอัปเดตคำตอบ :)
Ricky Boyce

11
  1. เรื่อง : ในการสมัครก็มักจะได้รับข้อมูลที่ถูกผลักหลังจากที่มันของการสมัครสมาชิกคือค่าผลักดันก่อนหน้านี้จะไม่ได้รับ
const mySubject = new Rx.Subject();

mySubject.next(1);

const subscription1 = mySubject.subscribe(x => {
  console.log('From subscription 1:', x);
});

mySubject.next(2);

const subscription2 = mySubject.subscribe(x => {
  console.log('From subscription 2:', x);
});

mySubject.next(3);

subscription1.unsubscribe();

mySubject.next(4);

จากตัวอย่างนี้นี่คือผลลัพธ์ที่จะพิมพ์ในคอนโซล:

From subscription 1: 2
From subscription 1: 3
From subscription 2: 3
From subscription 2: 4

สังเกตว่าการสมัครรับข้อมูลที่มาถึงล่าช้าจะพลาดข้อมูลบางส่วนที่ถูกส่งไปยังหัวเรื่องได้อย่างไร

  1. เล่นซ้ำหัวเรื่อง : สามารถช่วยได้โดยการเก็บบัฟเฟอร์ของค่าก่อนหน้าซึ่งจะส่งไปยังการสมัครสมาชิกใหม่

ต่อไปนี้เป็นตัวอย่างการใช้งานสำหรับการเล่นซ้ำหัวข้อที่buffer of 2 previous valuesมีการเก็บรักษาและส่งออกในการสมัครสมาชิกใหม่:

const mySubject = new Rx.ReplaySubject(2);

mySubject.next(1);
mySubject.next(2);
mySubject.next(3);
mySubject.next(4);

mySubject.subscribe(x => {
  console.log('From 1st sub:', x);
});

mySubject.next(5);

mySubject.subscribe(x => {
  console.log('From 2nd sub:', x);
});

นี่คือสิ่งที่ให้เราที่คอนโซล:

From 1st sub: 3
From 1st sub: 4
From 1st sub: 5
From 2nd sub: 4
From 2nd sub: 5
  1. Behavior subject : คล้ายกับ replay subject แต่จะฉายซ้ำเฉพาะค่าที่ปล่อยออกมาล่าสุดหรือเป็นค่าดีฟอลต์หากไม่มีการปล่อยค่าก่อนหน้านี้:
const mySubject = new Rx.BehaviorSubject('Hey now!');

mySubject.subscribe(x => {
  console.log('From 1st sub:', x);
});

mySubject.next(5);

mySubject.subscribe(x => {
  console.log('From 2nd sub:', x);
});

และผลลัพธ์:

From 1st sub: Hey now!
From 1st sub: 5
From 2nd sub: 5

อ้างอิง: https://alligator.io/rxjs/subjects/


4

จาก: Randall Koutnik หนังสือ“ Build Reactive Websites with RxJS” :

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

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

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


1

คำตอบที่ได้รับการโหวตส่วนใหญ่ผิดอย่างชัดเจนโดยอ้างว่า:

"หากคุณเริ่มต้นReplaySubjectด้วยขนาดบัฟเฟอร์ 1 มันจะทำงานเหมือนกับ a BehaviorSubject"


นี่ไม่เป็นความจริงทั้งหมด ตรวจสอบโพสต์บล็อกที่ยอดเยี่ยมนี้เกี่ยวกับความแตกต่างระหว่างทั้งสอง ตัวอย่างเช่นหากคุณสมัครรับค่าเสร็จสมบูรณ์BehaviorSubjectคุณจะไม่ได้รับค่าสุดท้าย แต่สำหรับค่าที่ReplaySubject(1)คุณจะได้รับค่าสุดท้าย

นี่คือความแตกต่างที่สำคัญที่ไม่ควรมองข้าม:

const behavior = new BehaviorSubject(null);
const replay = new ReplaySubject(1);

behavior.skip(1).subscribe(v => console.log('BehaviorSubject:', v));
replay.subscribe(v => console.log('ReplaySubject:', v));

behavior.next(1);
behavior.next(2);
behavior.complete();
behavior.subscribe(v => console.log('Late B subscriber:', v));

replay.next(1);
replay.next(2);
replay.complete();
replay.subscribe(v => console.log('Late R subscriber:', v));

ตรวจสอบตัวอย่างโค้ดนี้ที่นี่ซึ่งมาจากบล็อกโพสต์ดีๆในหัวข้อ


0
     // ***********Subject  concept ***********
    let subject = new Subject<string>();


    subject.next("Eureka");
    subject.subscribe((data) => {
      console.log("Subscriber 1 got data >>>>> "+ data);
    });
    subject.subscribe((data) => {
      console.log("Subscriber 2 got data >>>>> "+ data);
    });

       // ********behaviour subject*********
    // Behavior subjects need a first value
let subject1 = new BehaviorSubject<string>("First value");


subject1.asObservable().subscribe((data) => {
  console.log("First subscriber got data behaviour subject>>>>> "+ data);
});
subject1.next("Second value")
  • เรื่อง - ผู้สมัครสมาชิกจะได้รับค่าที่เผยแพร่หลังจากทำการสมัครสมาชิกแล้วเท่านั้น
  • BehaviorSubject - สมาชิกใหม่จะได้รับค่าที่เผยแพร่ล่าสุดหรือมูลค่าเริ่มต้นทันทีที่สมัครสมาชิก
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.