让我们考虑以下示例代码:
gude() {
const digestor$ = new Observable(subscriber => {
for (let i = 0; i < 4711; i++) {
setTimeout(() => {
const hash = createHashWithNLeadingZeroes(i);
subscriber.next(hash);
}, i);
}
});
const subscription = digestor$.subscribe(
_ => {
if (subscription) {
subscription.unsubscribe();
}
}
);
}
在函数 gude()中,创建了一个新的observable,该对象发出的哈希值将前 n 个前导值设置为零。观察者订阅了该可观察的内容,并立即取消了订阅。假设函数 createHashWithNLeadingZeroes()花费大量时间来生成响应。
恕我直言,以下情况正在发生:
(1)创建一个新的Observable,并将描述Observable行为的函数内部存储在属性 _subscribe (https://github.com/ReactiveX/rxjs/blob/master/src/internal/Observable.ts:37-41)中。
(2)调用 subscribe()时,首先将Observer包裹在 Subscriber 对象中,然后再应用 Subscriber _subscribe 函数,该函数保留了Observable的逻辑。 _subscribe()会快速返回,因为仅设置了4711超时,并返回了Subscription对象(https://github.com/ReactiveX/rxjs/blob/master/src/internal/Observable.ts:206-250)。
订阅服务器基本上拦截对 next(), error()和 complete()的调用,并且仅在以下情况下转发给实际的观察者:内部未设置属性 isStopped (https://github.com/ReactiveX/rxjs/blob/master/src/internal/Subscriber.ts:90-128)。
(3)一旦设置了变量 subscription ,就会调用 unsubscribe()。除其他外,这将导致将 isStopped 设置为true,以便订阅服务器不再将哈希转发给观察者(https://github.com/ReactiveX/rxjs/blob/master/src/internal/Subscriber.ts:130-136)。
根据该逻辑,可观察对象仍将继续执行其工作,直到无目的地计算了所有4711个哈希,因为观察者的方法变为无操作。最终,这种行为可能会影响应用程序的性能,具体取决于订阅量和Observable的工作量。我发现很难相信所描述的是正确的。我在这里想念哪一部分?
答案 0 :(得分:1)
我在哪部分想念?
我认为您缺少的部分是遵守合同的观察员的责任:如果要求其停止排放,则应停止排放。因此,您的观察对象应执行以下操作:
const digestor$ = new Observable(subscriber => {
let keepGoing = true;
for (let i = 0; i < 4711 && keepGoing; i++) {
setTimeout(() => {
if (keepGoing) {
const hash = createHashWithNLeadingZeroes(i);
subscriber.next(hash);
}
}, i);
return () => keepGoing = false; // this function is called when the subscriber unsubscribes
}
});
通常最好的方法是依靠现有的工厂功能和操作员来实现所需的行为。例如,您可以通过使用range()
,timer()
和map()
来实现与上述可观察到的等效。
答案 1 :(得分:1)
您正在创建"hot" Observable,因此即使没有订阅者,它也会发出。
使用new Observable()
创建Observable时,您可以选择返回所谓的拆解(或处理)函数,该函数应在需要时清理所有资源。
因此,您需要停止计时器。
new Observable(subscriber => {
...
const handler = setTimeout(() => {...});
return () => clearTimeout(handler);
});
或者,如果您有多个计时器,您将在所有计时器上调用clearTimeout
。