RxJs-concatMap替代方案,可将介于两者之间的所有内容

时间:2018-12-12 08:37:31

标签: rxjs

我正试图找到一个行为类似于concatMap的运算符,但是将两者之间的所有内容都删除。例如,concatMap执行以下操作:

  
      
  • 下一个
  •   
  • 开始处理
  •   
  • 下一个b
  •   
  • 下一个c
  •   
  • 完成处理
  •   
  • 开始处理b
  •   
  • 完成处理b
  •   
  • 开始处理c
  •   
  • 完成处理c
  •   

相反,我正在寻找一种将丢弃b的机制,因为c已经出现:

  
      
  • 下一个
  •   
  • 开始处理
  •   
  • 下一个b
  •   
  • 下一个c
  •   
  • 完成处理
  •   
  • (跳过b)
  •   
  • 开始处理c
  •   
  • 完成处理c
  •   

有关更多扩展的示例,请参见以下要点:https://gist.github.com/Burgov/afeada0d8aad58a9592aef6f3fc98543

4 个答案:

答案 0 :(得分:2)

我认为您要寻找的运营商是throttle

这里是工作中的Stackblitz。进行此工作的关键是设置配置对象,该配置对象将传递给throttle(),以使其能够{{1}发射和处理前导和尾随源发射,但忽略其间的任何发射。 }}正在运行。

这是Stackblitz的关键功能:

processData()

又矮又甜,除了一个问题外,它都能按要求工作...

更新:

上面的代码的“一个问题”是,当可观察到的源代码完成时,// 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() 取消订阅processData,从而有效地停止了所有需要完成的最终处理。正如Bart van den Burg在下面的评论中指出的那样,解决方法是使用主题。我认为有很多方法可以做到这一点,但是Stackblitz已更新为现在可以运行的以下代码:

throttle()

答案 1 :(得分:1)

这是我最简单的解决方案:

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);

实时演示:https://stackblitz.com/edit/rxjs-z33jgp?devtoolsheight=60

动作顺序是这样的:

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

这也不需要multicast运算符,但我想避免泄漏状态变量。没有它们似乎不可能完全实现,因此只有一个lastValue。此变量仅用于在用mockDelayedObs重新订阅同一链之后,忽略两次为同一值调用repeat()

答案 2 :(得分:0)

也许您可以在对race执行b之后立即尝试在cmergeMap上使用a方法?

我看起来像这样:

a.pipe(
  mergeMap(AResult => {
     // store aResult
     return race(b,c)
  }
).subscribe(
   finalResult => {
      // final result corresponding to either b or c
   }
)

如果您已经定义了要在a之后执行的通话,则可以使用此功能。

答案 3 :(得分:0)

Ph,这很难破解:

https://stackblitz.com/edit/angular-yk7akk

所以基本上,我创建了2个可观察对象:

  • immediateItems是可以立即执行的项目
  • postponedItems基于lastFinished $。它将发出被阻止执行的最后一个项目。

concatMap然后对这两个可观察对象进行合并。.

它按描述的方式工作,但它不完全是一种简单或直接的方法(命令性代码气味)。我一直在关注此讨论,以寻求更优雅的解决方案。