我想在RxJS中实现以下行为,但是找不到使用可用运算符的方法:
// Example usage.
streamA$
.pipe(
unknownOperator(streamB$, 800),
tap(val => doSomething(val))
)
// Output: E.g. [event1, event2, <skips processing because streamB$ emitted>, event10, ...]
// Operator API.
const unknownOperator = (pauseProcessingWhenEmits: Observable<any>, pauseIntervalInMs: number) => ...
我认为throttle
可以用于此用例,但是直到 B 首次发射(它永远不会!)之前,它不会让任何发射通过。
streamA$
.pipe(
// If B does not emit, this never lets any emission of A pass through!
throttle(() => streamB$.pipe(delay(800)), {leading: false}),
tap(val => doSomething(val))
)
一个简单的技巧是手动订阅B,在Angular组件中发射值时存储时间戳,然后过滤直到经过指定的时间:
(显然与避免反应框架的副作用相反)
streamB$
.pipe(
tap(() => this.timestamp = Date.now())
).subscribe()
streamA$
.pipe(
filter(() => Date.now() - this.timestamp > 800),
tap(val => doSomething(val))
)
在构建自己的自定义运算符之前,我想咨询一下这里的专家是否有人知道该运算符(组合)没有引入副作用:)
答案 0 :(得分:2)
我认为这是一种方法:
bModified$ = b$.pipe(
switchMap(
() => of(null).pipe(
delay(ms),
switchMapTo(subject),
ignoreElements(),
startWith(null).
)
)
)
a$.pipe(
multicast(
new Subject(),
subject => merge(
subject.pipe(
takeUntil(bModified$)
),
NEVER,
)
),
refCount(),
)
似乎这不是一个问题,解决方案必然涉及多播,但是在上述方法中,我使用了一种本地多播。
这不是预期的多播行为,因为如果您多次订阅a$
(比如说N次),则将到达N
次,因此多播在该级别上不会发生。
因此,让我们检查每个相关部分:
multicast(
new Subject(),
subject => merge(
subject.pipe(
takeUntil(bModified$)
),
NEVER,
)
),
第一个参数将指示用于实现该本地多播的主题类型。第二个参数是一个函数,更准确地称为选择器。它的单个参数是之前指定的参数(Subject
实例)。每次订阅a$
时都会调用此选择器功能。
我们从source code中可以看到:
selector(subject).subscribe(subscriber).add(source.subscribe(subject));
使用source.subscribe(subject)
订阅了源。通过selector(subject).subscribe(subscriber)
实现的是一个新的subscriber
,它将成为Subject
的观察者列表的一部分(它始终是相同的Subject
实例),因为merge
内部订阅提供的可观察对象。
我们使用merge(..., NEVER)
是因为,如果订阅选择器的订户完成了,那么下次a$
流再次变为活动状态时,将必须重新订阅源。通过附加NEVER
,调用select(subject)
的可观察结果表单将永远不会完成,因为要完成merge
,它的所有可观察项都必须完成。
subscribe(subscriber).add(source.subscribe(subject))
在subscribed
和Subject
之间建立连接,以便subscriber
完成时,Subject
实例将具有其unsubscribe
method叫。
因此,假设我们已订阅a$
:a$.pipe(...).subscribe(mySubscriber)
。正在使用的Subject
实例将有一个订阅者,并且如果a$
发出某些东西,mySubscriber
将(通过主题)收到它。
现在让我们讨论bModified$
发出时的情况
bModified$ = b$.pipe(
switchMap(
() => of(null).pipe(
delay(ms),
switchMapTo(subject),
ignoreElements(),
startWith(null).
)
)
)
首先,我们使用switchMap
是因为一个要求是当b$
发出时,计时器应该复位。但是,我认为这个问题的方式是,b$
发出 2件事:
a$
的排放量(2) (1)
通过在takeUntil
的订户中使用Subject
来实现。通过使用startWith
,b$
将立即发射,因此a$
的发射将被忽略。在switchMap
内部可观察的范围内,我们使用delay(ms)
指定计时器应花费的时间。经过它之后,switchMapTo(subject)
现在将在Subject
的帮助下获得新的订户,这意味着a$
将收到mySubscriber
的排放量(而不必重新订阅源)。最后,使用ignoreElements
是因为否则将在a$
发出时表示b$
也发出,这将导致a$
再次停止。 switchMapTo(subject)
之后是a$
的通知。
基本上,我们可以通过以下方式实现可暂停的行为:当Subject
实例作为一个订户(最多有 个)此解决方案),不暂停。如果没有,则表示已已暂停。
编辑:或者,您可以查看pause
operator中的rxjs-etc
。