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
สองสิ่ง:
อย่าคาดหวังว่าจะเข้าถึงค่าของ subject ได้ แทนที่จะทำเช่นนั้น คุณสามารถใช้รูปแบบทางเลือกของ
mergeMapซึ่งช่วยให้คุณสามารถเลือกค่าจาก source และ stream ใหม่ ในกรณีนี้ ฉันใช้มันเพื่อส่งคืนรายการใหม่ไปยัง stream โดยมีทั้งค่า source และค่า จาก inner observableคุณสามารถใช้เพียง
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);
}
