typescript - RXJS 的 takeUntil 和 switchMap 与定时器一起使用似乎无法完成内部定时器
我有以下代码:
private takeControlTaskRunning$ = new BehaviorSubject<boolean>(false);
private initTakeControlTask() {
this.takeControlTaskRunning$
.pipe(
takeUntil(this.dependencies.destroyed$),
switchMap((isRunning) => {
if (!isRunning) {
return of(false);
}
return timer(0, this.updatePeriodMilliseconds).pipe(
takeUntil(this.dependencies.destroyed$), <--- Why is this needed?
map(() => {
return true;
})
);
}),
filter((isRunning) => isRunning === true),
)
.subscribe(async () => {
await this.syncVideo();
});
}
public takeControl() {
this.takeControlTaskRunning$.next(true);
}
public stopControl() {
this.takeControlTaskRunning$.next(false);
}
当我在 fakeAsync 测试中运行此时,
describe('takeControl', () => {
it('runs the update task periodically', fakeAsync(async () => {
// Given an controller instance
const { controller, dependencies } = await create();
// When takeControl is called
await controller.takeControl();
// And some time passes
jasmine.clock().tick(1);
// Then the update task should be running
expect(
dependencies.updateSync as jasmine.Spy
).toHaveBeenCalle();
destroyed$.next();
flush();
}));
});
我收到以下错误:
Error: 1 periodic timer(s) still in the queue.
添加一个内部的 takeUntil 似乎可以解决问题,但我不明白为什么这是必要的。就我所理解,外部的 takeUntil 应该会完成 observable,而 switchMap 应该会完成内部的 timer,为什么情况不是这样呢?
答案#1
问题在于运算符的顺序。一般原则是,takeUntil() 应该总是在链中的最后一个运算符。
在你的情况下发生的是,this.takeControlTaskRunning$ 发出值,该值通过 switchMap 映射到 timer()。但是当 takeUntil() 发出 complete 通知时,它会被 switchMap 停止,因为 switchMap 仍在等待其内部 Observable(在这种情况下是 timer())完成。
如果你在 switchMap() 之后使用 takeUntil(),complete 通知将会到达观察者,这会触发取消订阅,即使 switchMap() 中的内部 Observable 仍在等待,也会立即被取消订阅。