Javascript 在RxJS中,我如何直接链接酒吧子系统中的生产者和消费者,而无需主题? 编辑

Javascript 在RxJS中,我如何直接链接酒吧子系统中的生产者和消费者,而无需主题? 编辑,javascript,system.reactive,reactive-programming,publish-subscribe,rxjs,Javascript,System.reactive,Reactive Programming,Publish Subscribe,Rxjs,我认为这一问题的实施相当复杂,至今已有3个月无法解决。我在这里用另一个问题重新表述了它:,希望它能更简洁地概括我想要的东西。谢谢大家的帮助 起初的 我现在已经学会了如何通过扫描将一系列部分应用的状态操作函数映射到我的状态来处理RxJS中的状态。然而,我现在想更进一步,通过数据平面(pubsub层)在“生产者”和“消费者”之间声明性地连接流,而不通过主题进行管道传输 网上有很多教程介绍了这一点,但它们基本上都只是将publish强制用作主题。下一个是每个流名称或频道的中间层。这是我当前的实现,但它

我认为这一问题的实施相当复杂,至今已有3个月无法解决。我在这里用另一个问题重新表述了它:,希望它能更简洁地概括我想要的东西。谢谢大家的帮助

起初的 我现在已经学会了如何通过扫描将一系列部分应用的状态操作函数映射到我的状态来处理RxJS中的状态。然而,我现在想更进一步,通过数据平面(pubsub层)在“生产者”和“消费者”之间声明性地连接流,而不通过主题进行管道传输

网上有很多教程介绍了这一点,但它们基本上都只是将
publish
强制用作主题。下一个是每个流名称或频道的中间层。这是我当前的实现,但它需要为每个永不结束的流创建一个ReplaySubject(1)(用于缓存延迟到达的消费者的值),因为任何获得该流的消费者都会收到一个引用,如果在当前没有生产者流到该名称的频道时删除主题,则该引用将无效

我想直接连接流,让消费者接收流,这些流是特定名称的所有活动已发布流的集合。这仍然需要在初始注册生产者流(格式为
{name,stream}
的生产者流的传入流)中使用Subject to pipe

然后,我想将所有相同的命名流合并到一个单独的流中,每个注册消费者都会收到该流作为引用(设想一个过滤器服务接收到对
filter.add
,它是所有活动生产者创建过滤器的合并)-但要重新合并该流,以反应式的方式,且消费者与该流的链接仍然有效,前提是具有相同名称的新生产者也注册。任何延迟到达的使用者也应该接收该聚合流的最后一个缓存值

通过这种方式,每次在pubsub层上公开新流时,都需要动态地重新评估每个聚合流,因此在“getActive”函数(like)中包装流并不起作用,因为这是必需的,并且只在首次获取流时发生一次,而不是在每次发布新流时为所有消费者重新评估

结果应该是一个流:

  • 永不完成
  • 聚合某个名称的所有活动流的结果,并丢弃不再活动的流
  • 延迟更新已接收到对该流的引用的所有使用者,以便在发布同名的新流时,该使用者的引用保持有效,并重新评估以合并到该新流中
  • 缓存上次合并的结果,以便新使用者将在订阅时收到上次发出的值
基本上,我需要“TrimtoActivity”功能

function getStream(all$, name) {
  return all$
  .filter(x => x.name === name)
    .map(x => x.stream)
    .map(trimToActiveOnly) // in this way, should be dynamically re-evaluated for all
                 // consumers every time new stream is published or stream ends,
                 // not just run once when the stream is first 'got'
    .flatMapLatest(x => Rx.Observable.merge(x)) // where x is all currently active streams for a particular name, with finished/errored ones discarded
    .publish().refCount(); //this is re-evaluated when a new stream is published or when one of the aggregated streams concludes. So the aggregate stream itself never concludes but may go cold if nothing is subscribed.
}

// desired behavior as followed
const publishStream$ = new Rx.Subject();

const foo$ = getStream(publishStream$, 'foo');
const bar$ = getStream(publishStream$, 'bar');

const fooSourceA$ = new Rx.Subject();
const fooSourceB$ = new Rx.Subject();
const barSourceA$ = new Rx.Subject();
const barSourceB$ = new Rx.Subject();
publishStream$.onNext({ name: 'foo', stream: fooSourceA$ });
publishStream$.onNext({ name: 'foo', stream: fooSourceB$ });
publishStream$.onNext({ name: 'bar', stream: barSourceA$ });
fooSourceA$.onNext('hello');
fooSourceA$.onNext('world');
barSourceA$.onNext('testing');

const fooSub = foo$.subscribe(x => console.log('foo: ' + x)); // should receive cached 'world'
const barSub = bar$.subscribe(x => console.log('bar: ' + x)); // should receive cached 'testing'

publishStream$.onNext({ name: 'bar', stream: barSourceB$ });
barSourceB$.onNext('123'); // barSub should now receive '123' as the aggregated active streams are dynamically re-evaluated on each publish of a new stream!

我也有一个关于这个的JSBin。

它并不完全相同,但它与

您有一个可观察的
。您可以对其应用
filter
,将其减少为仅满足特定条件的子流。然后,您可以使用
映射
排除名称,以获得一个
可观察的
,并且从那里您可以使用您喜欢的任何缩减来组合值(例如,
zip
按索引匹配,
combinelatetest
在子流产生值时重新发出某种聚合,或
flatMapLatest
展平为单个流)。我想您可能希望在链接中使用类似于
活动
转换的内容,因为您需要将已完成的流踢出,因为它们会干扰许多组合方法

例如,考虑以下方法:

function getStream(all$, name) {
  return active(all$.filter(x => x.name === name).map(x => x.stream))
    .flatMapLatest(x => Rx.Observable.merge(x));
}
从这里,您可以这样设置:

const publishStream$ = new Rx.Subject();
const foo$ = getStream(publishStream$, 'foo');
const bar$ = getStream(publishStream$, 'bar');
然后您可以订阅:

const fooSub = foo$.subscribe(x => console.log('foo: ' + x));
const barSub = bar$.subscribe(x => console.log('bar: ' + x));
您可以将数据推送(您不会真正使用主题,这只是一个示例):

如果我正确理解了您想要的内容,这就可以了。为了解释这里发生的事情,
getStream
获取一个包含名称和流的对象流,只过滤到具有指定名称的对象,丢弃名称并仅保留流,然后通过t中的
active
函数运行它他链接了答案,其功能类似于扫描缩减。结果将是流的数组流,我们随后使用
flatMapLatest
merge
将其扁平化为单个流。这意味着给定名称的所有流将聚合为单个流,可以根据需要订阅

以下是整个工作流程:


一个警告是,如果使用
flatMapLatest
merge
作为
active
的输出,这样做将不会很好地工作,因为每次发出一组active observates时,订阅都会重新开始。

感谢您的评论。您提供的答案非常接近(另外您的另一个答案也很好),但这涉及到在某些流完成后保持流活动(我也希望如此!)但我不能让它在消费者已经获得流之后新的生产者到达时重新更新。我已经编辑并更新了我的代码,非常感谢您的帮助。我已经用一个例子更新了我的答案。我希望我已经正确理解了您要找的内容,并且这个例子有帮助。这个例子有“消费者”在“生产者”面前登记,一切正常。我仍然可以从订阅函数的末尾看到一个可观察的结果,而不是实际的数据。我必须对RxJS 5的使用进行一些更改,所以可能是我使用了“订阅者”而不是“一次性的”?这是迁移文档所说的,我看不到其他任何东西
const fooSourceA$ = new Rx.Subject();
const fooSourceB$ = new Rx.Subject();
const barSourceA$ = new Rx.Subject();
const barSourceB$ = new Rx.Subject();
publishStream$.onNext({ name: 'foo', stream: fooSourceA$ });
publishStream$.onNext({ name: 'foo', stream: fooSourceB$ });
publishStream$.onNext({ name: 'bar', stream: barSourceA$ });
publishStream$.onNext({ name: 'bar', stream: barSourceB$ });
fooSourceA$.onNext('hello');
fooSourceA$.onNext('world');
barSourceA$.onNext('testing');
barSourceB$.onNext('123');