ค้นหาเว็บไซต์

angular - วิธีหลีกเลี่ยงการสมัครสมาชิก rxJS ซ้อน


ฉันเริ่มโปรเจกต์ Angular 7 ใหม่ซึ่งฉันจำเป็นต้องเรียก API เพื่อดึงรายการผลลัพธ์ แล้วถ้าผลลัพธ์เฉพาะอยู่ในรายการฉันจะเลือกมัน ถ้าไม่อยู่ฉันจะสร้างมันโดยเรียก API endpoint อื่น ทุกอย่างนี้ควรทำในเมธอด ngOnInit()

เพื่อทำให้ปัญหาง่ายขึ้น ฉันมีโค้ดดังต่อไปนี้:

const subject = new BehaviorSubject(null);
subject.next(6);

const res$ = subject.pipe(
  mergeMap(() => getValues()),
  tap((res) => {
    const exists = res.find(x => subject.value === x) || null;
    if (exists === null) {
      // TODO: call the getNew() function, but I don't want nested call
    } else {
      // Do nothing
    }
  })
);

// res$ should contains [1, 2, 3, 4, 5, 6]
res$.subscribe(x => console.log(x));

function getValues() {
  // Mocking the API call
  return of([1, 2, 3, 4, 5]);
}

function getNew() {
  // Mocking the API call
  return of(6);
}

สุดท้ายแล้ว observable res$ ควรจะมีอาเรย์ทั้งหมด (พร้อมค่าที่เพิ่มเข้าไป) และฉันต้องการหลีกเลี่ยงการ subscribe แบบซ้อนกัน

ขอบคุณมาก!

คำตอบ #1

สองสิ่ง:

  1. อย่าคาดหวังว่าจะเข้าถึงค่าของ subject ได้ แทนที่จะทำเช่นนั้น คุณสามารถใช้รูปแบบทางเลือกของ mergeMap ซึ่งช่วยให้คุณสามารถเลือกค่าจาก source และ stream ใหม่ ในกรณีนี้ ฉันใช้มันเพื่อส่งคืนรายการใหม่ไปยัง stream โดยมีทั้งค่า source และค่า จาก inner observable

  2. คุณสามารถใช้เพียง mergeMap อีกตัวหนึ่งเพื่อคืนค่าผลลัพธ์เดิมหรือผลลัพธ์จากการเรียก API ใหม่โดยการห่อผลลัพธ์เดิมใน of เพื่อสร้าง observable ภายในใหม่

const res$ = subject.pipe(
  mergeMap(x => getValues(), (orig, res) => ({ orig, res })),
  mergeMap(({ orig, res }) => {
    const exists = res.find(x => x === orig) || null;
    return (exists) 
      ? of(res)
      : getNew().pipe(map(x => [... res, x]))
  })
);

สแตกบลิทซ์

คำตอบ #2

วิธีหนึ่งในการแก้ปัญหานี้อาจเป็นการแบ่งสิ่งนี้ออกเป็น observable สองอัน จากนั้นรวมมันอีกครั้งด้วย combineLatest เราจะใช้ observable resFirst$ สองครั้งและกรองมันขึ้นอยู่กับว่ามันว่างหรือไม่

import { combineLatest } from 'rxjs';

---

const subject = new BehaviorSubject(null);
subject.next(6);

const resFirst$ = subject.pipe(
    mergeMap(() => getValues()),
    map((res) => res.find(x => subject.value === x) || null)
);

const resSecond$ = resFirst$.pipe(
    filter(exists => exists === null), <--- FILTERING FOR IS NULL
    mergeMap(() => getNew()) <--- SWITCHMAP COULD ALSO BE USED HERE DEPENDING ON WHAT YOU WANT
);

const final$ = combineLatest(resFirst$, resSecond$)
    .pipe(filter(exists => exists !== null)) <--- FILTERING FOR NOT NULL
    .subscribe(x => console.log(x));

function getValues() {
    // Mocking the API call
    return of([1, 2, 3, 4, 5]);
}

function getNew() {
    // Mocking the API call
    return of(6);
}