rxjs switchMap需要返回订阅的可观测数据
以下是要求: 单击开始按钮时,每100ms发射事件x次,每次发射对应一次UI更新。当x次发射完成时,它将触发最终的UI更新,看起来很简单吧 这是我的密码:rxjs switchMap需要返回订阅的可观测数据,rxjs,observable,Rxjs,Observable,以下是要求: 单击开始按钮时,每100ms发射事件x次,每次发射对应一次UI更新。当x次发射完成时,它将触发最终的UI更新,看起来很简单吧 这是我的密码: const start$ = fromEvent(document.getElementById('start'), 'click') const intervel$ = interval(100) .pipe( take(x), share() ) var startLight$ = star
const start$ = fromEvent(document.getElementById('start'), 'click')
const intervel$ = interval(100)
.pipe(
take(x),
share()
)
var startLight$ = start$
.pipe(
switchMap(() => {
intervel$
.pipe(last())
.subscribe(() => {
// Update UI
})
return intervel$
}),
share()
)
startLight$
.subscribe(function (e) {
//Update UI
})
显然,subscribe insideswitchMap
是反模式的,所以我尝试重构代码:
const startInterval$ = start$
.pipe(
switchMapTo(intervel$),
)
startInterval$.pipe(last())
.subscribe(() => {
//NEVER Receive value
})
const startLight$ = startInterval$.pipe(share())
问题是intervel$流是在switchMap
内部生成的,不能在外部访问,您只能访问生成interval$的流,即start$,它永远不会完成
是否有更聪明的方法来处理此类问题,或者这是
rxjs
的固有限制?将更新逻辑放入开关映射中和tap()
,tap将运行多次,subscribe()只执行最后一次发射
你很接近。在intervel$中使用last()只向下面的订阅发送最后一个。工作以下是StackBlitz的详细信息:
const start$ = fromEvent(document.getElementById('start'), 'click');
const intervel$ = interval(100)
.pipe(
tap(() => console.log('update UI')), // Update UI here
take(x),
last()
);
const startInterval$ = start$
.pipe( switchMapTo(intervel$));
startInterval$
.subscribe(() => {
console.log('will run once');
});
更新
如果您不希望使用tap()
,那么您可以通过仅获取第一次发射,然后使用take(1)
或first()
完成,从而使start$完成。展示这个
const start$ = fromEvent(document.getElementById('start'), 'click')
.pipe(
first()
);
const intervel$ = interval(100)
.pipe(
take(x)
);
const startInterval$ = start$
.pipe(
switchMapTo(intervel$)
);
startInterval$
.subscribe(
() => console.log('Update UI'),
err => console.log('Error ', err),
() => console.log('Run once at the end')
);
这种方法(或完成可观察对象的任何方法)的缺点是,一旦完成,它就不会被重用。例如,在新的StackBlitz中多次单击按钮将不起作用。使用哪种方法(第一种可以反复点击的方法或完成的方法)取决于您需要的结果
另一种选择
创建两个intervel$
可观察对象,一个用于中间用户界面更新,另一个用于最终用户界面更新。将它们合并在一起,只在订阅中更新UI。对于此选项
代码:
一种更惯用的方式,与前一种逻辑相同(Guichi)
反射
原始问题的关键问题是“以不同的方式使用相同的观察值”,即在进度和最终阶段。因此,merge
是针对此类问题的一种相当不错的逻辑模式@Fan Cheung谢谢你们两位,你们基本上提供了保存解决方案!他们肯定很有帮助。然而,有人认为“将重要的副作用从do移到subscribe”[,这是一个不错的建议还是可以实现的方法?而且,我认为subject
可以解决这个问题。但是subject
让我感到陌生。只是一个模糊的想法,它可能会起作用。有可能充实吗?我很少在subscribe()中添加任何内容,因为流应该是可重用的、可组合的。在开发功能时,大多数时候你需要改进和重构流的片段,如果你想要额外的逻辑,只需链接和扩展流,并将其另存为另一个流。@Guichi-我在上面添加了一个更新,其中包含一个替代解决方案,可观察的完成。是的,正如我所提到的,这是完成一个可观察的界面的缺点。好的,我的最后一个想法如下:创建两个intervel,一个用于中间UI更新,一个用于最终UI更新。将它们合并在一起,并仅在订阅中更新UI,不点击:
const start$ = fromEvent(document.getElementById('start'), 'click')
.pipe(
first()
);
const intervel$ = interval(100)
.pipe(
take(x)
);
const startInterval$ = start$
.pipe(
switchMapTo(intervel$)
);
startInterval$
.subscribe(
() => console.log('Update UI'),
err => console.log('Error ', err),
() => console.log('Run once at the end')
);
const start$ = fromEvent(document.getElementById('start'), 'click')
const intervel1$ = interval(100)
.pipe(
take(x)
);
const intervel2$ = interval(100)
.pipe(
take(x+1),
last(),
mapTo('Final')
);
const startInterval$ = start$
.pipe(
switchMapTo(merge(intervel1$, intervel2$))
);
startInterval$
.subscribe(
val => console.log('Update UI: ', val)
);
import { switchMapTo, tap, take, last, share, mapTo } from 'rxjs/operators';
import { fromEvent, interval, merge } from 'rxjs';
const x = 5;
const start$ = fromEvent(document.getElementById('start'), 'click');
const intervel$ = interval(100);
const intervel1$ = intervel$
.pipe(
take(x)
);
const intervel2$ = intervel1$
.pipe(
last(),
mapTo('Final')
);
const startInterval$ = start$
.pipe(
switchMapTo(merge(intervel1$, intervel2$))
);
startInterval$
.subscribe(
val => console.log('Update UI: ', val)
);