【发布时间】:2019-07-15 03:43:41
【问题描述】:
我有一个可观察的源,当您单击时会发出。我想制作一个像下一个大理石图一样发出的运算符...
也就是说,它在第一次点击时发出。然后,当你有 2 个项目时它会发射,然后当你有 3 个项目时它会发射,等等。
我知道可以使用扫描和过滤运算符来完成,但我的问题是,是否可以使用 bufferWhen 运算符来做到这一点?
我尝试这样做(如this),但结果不是我所期望的(第一次点击不会立即发出)。
【问题讨论】:
我有一个可观察的源,当您单击时会发出。我想制作一个像下一个大理石图一样发出的运算符...
也就是说,它在第一次点击时发出。然后,当你有 2 个项目时它会发射,然后当你有 3 个项目时它会发射,等等。
我知道可以使用扫描和过滤运算符来完成,但我的问题是,是否可以使用 bufferWhen 运算符来做到这一点?
我尝试这样做(如this),但结果不是我所期望的(第一次点击不会立即发出)。
【问题讨论】:
它的工作原理如下代码。尽量避免在bufferWhen 中重复使用源代码,可能存在竞争条件
const source$ = fromEvent(document, 'click').pipe(
map(_ => new Date())
);
interval(1000).pipe(
tap(d => console.log('CLICK:', d)),
bufferWhen(() => {
return source$
})
).subscribe(console.log);
【讨论】:
我找到了使用groupBy 运算符的解决方案。
Stackblitz 中的演示。
代码如下:
function emitSequentially<T>(source$: Observable<T>) {
return defer(() => {
let emitWhen = 1;
return source$.pipe(
groupBy(_ => emitWhen),
mergeMap(group$ =>
group$.pipe(
take(emitWhen),
toArray(),
tap(_ => ++emitWhen)
)
)
);
});
}
const source1$ = fromEvent(document, 'click').pipe(
scan<Event, number>(acc => ++acc, 0)
);
const source2$ = interval(500).pipe(
scan(acc => ++acc, 0)
);
emitSequentially(source1$).subscribe(x => console.log('Emitting from click: ', x));
setTimeout(() => {
emitSequentially(source2$).subscribe(x => console.log('Emitting from interval: ', x));
}, 1000);
【讨论】: