RxJs-concatMap替代方案,将所有内容都放在两者之间
我试图找到一个行为类似于RxJs-concatMap替代方案,将所有内容都放在两者之间,rxjs,Rxjs,我试图找到一个行为类似于concatMap的操作符,但在两者之间删除所有内容。例如,concatMap执行以下操作: 下一个 开始处理 下一个b 下一个c 处理完一个 开始处理b 完成处理b 开始处理c 完成处理c 取而代之的是,我正在寻找一种机制,它将丢弃b,因为c已经出现了: 下一个 开始处理 下一个b 下一个c 处理完一个 (跳过b) 开始处理c 完成处理c 有关更详细的示例,请参见此要点:在a上执行mergeMap之后,您可以尝试在b上使用race方法 我看起来像这样: a.pi
concatMap
的操作符,但在两者之间删除所有内容。例如,concatMap执行以下操作:
- 下一个
- 开始处理
- 下一个b
- 下一个c
- 处理完一个
- 开始处理b
- 完成处理b
- 开始处理c
- 完成处理c
b
,因为c
已经出现了:
- 下一个
- 开始处理
- 下一个b
- 下一个c
- 处理完一个
- (跳过b)
- 开始处理c
- 完成处理c
有关更详细的示例,请参见此要点:在
a
上执行mergeMap
之后,您可以尝试在b
上使用race
方法
我看起来像这样:
a.pipe(
mergeMap(AResult => {
// store aResult
return race(b,c)
}
).subscribe(
finalResult => {
// final result corresponding to either b or c
}
)
如果您在
a
之后要执行的调用数已经定义,那么这将起作用。这是我能够做出的最简单的解决方案:
const source = new Subject();
const start = new Date();
const mockDelayedObs = val => of(val).pipe(delay(1200));
source.pipe(
multicast(
new ReplaySubject(1),
subject => {
let lastValue;
return subject.pipe(
filter(v => v !== lastValue),
exhaustMap(v => {
lastValue = v;
return mockDelayedObs(v);
}),
take(1),
repeat(),
);
}
),
)
.subscribe(v => {
console.log(new Date().getTime() - start.getTime(), v);
});
setTimeout(() => source.next(0), 0);
setTimeout(() => source.next(1), 500);
setTimeout(() => source.next(2), 1000);
setTimeout(() => source.next(3), 1500);
setTimeout(() => source.next(4), 1800);
setTimeout(() => source.next(5), 4000);
现场演示:
行动顺序如下:
next 0
start handling 0
next 1
next 2
finish handling 0
start handling 2
next 3
next 4
finish handling 2
start handling 4
finish handling 4
start handling 5
finish handling 4
因此,只会打印0、2、4和5
这在没有
多播
操作符的情况下也可以工作,但我希望避免泄漏状态变量。似乎没有它们是不完全可能的,所以只有一个lastValue
。此变量仅用于在使用repeat()
重新订阅同一链后,忽略对相同值调用两次mockDelayedObs
,这是一个很难破解的问题:
因此,基本上,我创建了两个观察值:
- 即时性是指可以立即执行的项目
- 延迟数据项基于lastFinished$。它将发出阻止执行的最后一项
它的工作原理如前所述,但它并不是一个简单或直接的方法(命令式代码)。我正在关注这个讨论,寻找更优雅的解决方案。我想你要找的操作员是
油门
这是一张工作票。实现这一点的关键是设置传递给throttle()
的配置对象,该对象允许它在processData()
运行期间发射(并处理)前导和尾随源排放,但忽略两者之间的任何排放
以下是Stackblitz的关键功能:
// Use 'from' to emit the above array one object at a time
const source$ = from(sourceData).pipe(
// Simulate a delay of 'delay * delayInterval' between emissions
concatMap(data => of(data).pipe(delay(data.delay * delayInterval))),
// Now tap in order to show the emissions on the console.
tap(data => console.log('next ', data.emit)),
// Finally, throttle as long as 'processData' is handling the emission
throttle(data => processData(data), { leading: true, trailing: true }),
).subscribe()
短而甜,除一期外,可按要求使用
更新:
上述代码的“一个问题”是,当源observable完成时,throttle()
取消了对processData的订阅,从而有效地停止了任何需要完成的最终处理。正如巴特·范登堡(Bart van den Burg)在下面的评论中指出的那样,解决办法是使用一个主题。我认为有很多方法可以做到这一点,但是Stackblitz已经更新了以下代码,现在可以工作了:
// Set up a Subject to be the source of data so we can manually complete it
const source$ = new Subject();
// the data observable is set up just to emit as per the gist.
const dataSubscribe = from(sourceData).pipe(
// Simulate a delay of 'delay * delayInterval' before the emission
concatMap(data => of(data).pipe(delay(data.delay * delayInterval))),
).subscribe(data => {
console.log('next ', data.emit); // log the emission to console
source$.next(data); // Send this emission into the source
});
// Finally, subscribe to the source$ so we can process the data
const sourceSubscribe = source$.pipe(
// throttle as long as 'processData' is handling the emission
throttle(data => processData(data), { leading: true, trailing: true })
).subscribe(); // will need to manually unsubscribe later ...
为什么它不应该在
a
完成之前就跳过c
,但是它跳过了b
?所以基本的想法是:“将服务器与对象的这个新状态同步,但要等到上一个请求完成”。最新的始终是唯一相关的,但必须始终处理最新的,以避免丢失数据。起初,我在想exhaustMap,它确实忽略了中间事件,但随后你当然也会丢失c…虽然这不是一个定义的数字,但我的示例可以包括整个字母表,如果处理操作的数量可能在2到26之间,这取决于nexts的速度和处理时间,我不确定是否完全理解您正在尝试做什么。您能在示例中再添加一个或两个字母来确定吗?你想处理第一个可观察的对象,然后从剩下的所有对象中取第一个吗?我在postHey中添加了一个更扩展的示例,这太完美了!而且非常优雅简单,很棒。如果我将它连接到一个主题(在实际项目中就是这样),而不是一个完成的可观察对象,那么它实际上是正常工作的。看见谢谢@巴登堡-脸掌-我应该想到一个主题!我不认为你对Stackblitz的编辑“占了上风”-你可能需要把它交出来编辑你自己的副本。我编辑了Stackblitz,并将更新答案。良好的团队合作!:)+我不知道节流阀中的配置存在。然而,在发出源值而不是切换的可观测值的意义上,它的行为是否与concatMap有根本的不同?@IngoBürk-是的,这是事实,可观测链在调用processData()
之后未被使用,因为我假设函数消耗并使用OP要查找的所有数据。但是您是对的,如果需要查看从processData()
链下游(例如subscribe()
内部)实际返回的值,此解决方案将无法按原样工作。@Bart-您需要processData()返回的值吗
例如更新视图等?如果是这样,则必须使用processData().pipe(/*执行某些操作*/)
对其进行管道传输,因为它们不是从节流()发出的。
。