我刚刚意识到,即使外部可观察对象没有订阅,内部可观察对象(例如在mergeMap或switchMap运算符中定义的那些对象)也不会“停止”。
举个更好的例子,让我们展示一些代码:
const {
Subject,
of: obsOf,
concat: obsConcat,
defer,
} = require("rxjs");
const {
finalize,
mergeMap,
tap,
takeUntil,
} = require("rxjs/operators");
const subject = new Subject();
obsOf(null).pipe(
mergeMap(() =>
obsConcat(
defer(() => {
console.log("side-effect 1");
return obsOf(1);
}),
defer(() => {
console.log("side-effect 2");
return obsOf(2);
}),
defer(() => {
console.log("side-effect 3");
return obsOf(3);
})
)
),
finalize(() => {
console.log("finalized");
})
)
.pipe(
takeUntil(subject),
tap((i) => {
if (i === 2) {
subject.next();
}
})
).subscribe(
(i) => { console.log("next", i); },
(e) => { console.log("error", e); },
() => { console.log("complete"); },
);
// Ouput:
// > side-effect 1
// > next 1
// > side-effect 2
// > complete
// > finalized
// > side-effect 3
记录了side-effect 3
行的事实很奇怪,因为外部可观察到的已经叫finalize
。
由于所有这些副作用都在defer
中,因此可以在取消订阅后完全避免它们。从我的角度来看,这些副作用根本没有任何价值。
是否知道为什么RxJS仍执行那些代码?
答案 0 :(得分:0)
不幸的是,这是设计使然(从RxJS 6开始)-concat
将缓冲可观察对象,并且即使您取消订阅也将订阅每个缓冲的对象(如果订阅为closed
,它将立即订阅退订)。
您必须防止可观察对象被缓冲...
obsOf(null).pipe(
mergeMap(() => obsOf(
defer(() => {
console.log("side-effect 1");
return obsOf(1);
}),
defer(() => {
console.log("side-effect 2");
return obsOf(2);
}),
defer(() => {
console.log("side-effect 3");
return obsOf(3);
})
)),
concatAll(),
finalize(() => {
console.log("finalized");
}),
takeUntil(subject),
tap((i) => {
if (i === 2) {
subject.next();
}
})
).subscribe(
(i) => { console.log("next", i); },
(e) => { console.log("error", e); },
() => { console.log("complete"); },
);
一个人可能认为上面的代码有效,但是直到您延迟了其中一个可观察对象。将obsOf(1)
替换为timer(100).pipe(mapTo(1));
,其行为完全相同。
唯一的解决方法是确保您不缓冲任何内容(意味着不使用concat*
运算符)或以其他方式限制可观察的生产(使用单独的Subject并手动控制生产)。