[英]How can I directly link producers and consumers in a pub sub system, without subjects, in RxJS?
I think the implementation of this question is reasonably complex, and have not been able to figure it out for 3 months now. 我认为这个问题的实施相当复杂,并且已经有3个月了。 I have re-phrased it in another question here: RxJS5 - How can I cache the last value of a aggregate stream, without using Subjects and whilst excluding those that have completed?
在这里,我在另一个问题中对此进行了重新表述: RxJS5-如何在不使用Subjects且排除已完成的主题的情况下,缓存聚合流的最后一个值? , which hopefully summarizes what I am after a little more concisely.
,希望可以更简洁地总结一下我的想法。 Thank you everyone for your help!
谢谢你们每一个人的帮助!
I have now learnt how to deal with state in RxJS by mapping a series of partially applied state operation functions onto my state via scan. 现在,我已经学习了如何通过扫描将一系列部分应用的状态操作函数映射到我的状态上,从而处理RxJS中的状态。 However, I would now like to take this a step further and connect streams declaratively between 'producers' and 'consumers' over a dataplane (pubsub layer), without piping them through a Subject.
但是,我现在想更进一步,并通过数据平面(pubsub层)以声明方式在“生产者”和“消费者”之间连接流,而不用通过主题传递它们。
There are many tutorials online covering this, but they all just basically use publish
imperatively as Subject.next in a middle layer for each stream name or channel. 在线上有很多教程对此进行了介绍,但是它们基本上只是在每个流名称或通道的中间层中强制性地将
publish
用作Subject.next。 This is my current implementation, but it requires creating a ReplaySubject(1) (for caching of values for late arriving consumers) for each stream that never ends, as any consumers getting the stream receive a reference that would be then invalid if Subjects were removed when there were no producers currently streaming to a channel of that name. 这是我当前的实现,但是它需要为每个永不结束的流创建一个ReplaySubject(1)(用于为较晚到达的消费者缓存值),因为任何获得该流的消费者都会收到一个引用,如果删除了Subject,该引用将无效当前没有生产者流到该名称的频道时。
I want to connect streams directly and have consumers taking in streams that are the aggregate of all active published streams of a particular name. 我想直接连接流,并让使用者使用流,该流是特定名称的所有活动已发布流的聚合。 This still requires a Subject to pipe in the initial registering producer streams (an incoming stream of producer streams in the format
{name, stream}
). 仍然需要在初始注册生产者流(格式为
{name, stream}
的生产者流的传入流)中使用Subject to管道。
I want to then merge all same-named streams into a single one that each registering consumer receives as a reference (think a filter service receiving a reference to filter.add
, which is a merge of all active producers creating filters) - but have this stream re-merge, in a reactive manner and with the consumer's link to that stream still valid, if a new producer with the same name also registers. 然后,我想将所有同名流合并为一个流,每个注册使用者都将其作为参考接收(想想一个过滤服务接收到
filter.add
的引用,这是所有创建过滤器的活动生产者的合并)-但是如果还注册了一个具有相同名称的新生产者,则以一种反应性的方式重新合并流,并且消费者到该流的链接仍然有效。 Any late arriving consumer should also receive the last cached value of that aggregate stream. 任何迟到的使用者也应接收该聚合流的最后一个缓存值。
In this way each aggregate stream needs to be dynamically re-evaluated each time a new stream is exposed on the pubsub layer, so wrapping a stream in a 'getActive' function (like here ) doesn't work as this is imperative and only happens once when the stream is first fetched, rather than lazily re-evaluated for all consumers every time a new stream is published. 这样,每次在pubsub层上公开新流时,都需要动态地重新评估每个聚合流,因此将流包装在“ getActive”函数中(如此处 )不起作用,因为这是必须的,并且只会发生在首次获取流时进行一次,而不是每次发布新流时都对所有消费者进行重新评估。
The result should be a stream that: 结果应该是流:
Basically, I need the 'trimToActiveOnly' function. 基本上,我需要'trimToActiveOnly'函数。
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!
It's not exactly the same, but this bears similarities to Rx: a zip-like operator that continues after one of the streams ended? 它并不完全相同,但这与Rx具有相似之处:像zip一样的运算符在其中一个流结束后是否继续?
You've got an Observable<{name, <Observable>}>
. 您有一个
Observable<{name, <Observable>}>
。 You can apply filter
to that to reduce it to only the substream that meets a certain criteria. 您可以对此应用
filter
,以将其减少到仅满足特定条件的子流。 You can then use map
to exclude the name get to an Observable<Observable>
, and from there you can use any reduction you like to combine the values (eg zip
to match up by index, combineLatest
to re-emit some kind of aggregate whenever a substream yields a value, or flatMapLatest
to flatten into a single stream). 然后,您可以使用
map
排除名称得到一个Observable<Observable>
,并从那里你可以使用任何减少,你喜欢的值相结合(如zip
通过指数投其所好, combineLatest
要重新发出某种聚合时子流产生一个值,或flatMapLatest
以展平为单个流)。 I imagine you'll want to use something like the active
transformation in the link, as you'll need to kick completed streams out as they will interfere with many of the combining methods. 我想您会在链接中使用诸如
active
Transformation之类的东西,因为您将需要踢掉已完成的流,因为它们会干扰许多合并方法。
For example, consider the following method: 例如,考虑以下方法:
function getStream(all$, name) {
return active(all$.filter(x => x.name === name).map(x => x.stream))
.flatMapLatest(x => Rx.Observable.merge(x));
}
From here, you could set things up like so: 在这里,您可以像这样设置:
const publishStream$ = new Rx.Subject();
const foo$ = getStream(publishStream$, 'foo');
const bar$ = getStream(publishStream$, 'bar');
You could then subscribe: 然后,您可以订阅:
const fooSub = foo$.subscribe(x => console.log('foo: ' + x));
const barSub = bar$.subscribe(x => console.log('bar: ' + x));
And you could push data through (you wouldn't really use subjects, this is just for example): 您可以推送数据(您不会真正使用主题,这只是举例):
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');
If I've understood what you want correctly, this will do the job. 如果我已正确理解您的要求,则可以完成此工作。 To explain what's going on here,
getStream
takes a stream of objects that contain a name and a stream, filters to only those objects with the specified name, throws the name away and retains just the stream, and runs it through the active
function from the linked answer, which will function like a scan reduction. 为了解释这是怎么回事就在这里,
getStream
需要包含一个名字和一个流,过滤器,只使用指定名称的对象的对象流,投名远,并保留紧邻流,并运行它通过active
从功能链接的答案,其作用类似于缩小扫描。 The result will be a stream of arrays of streams, which we subsequently flatten into a single stream using flatMapLatest
and merge
. 结果将是
flatMapLatest
的流数组,我们随后使用flatMapLatest
和merge
其扁平化为单个流。 This means that all streams for the given name will be aggregated into a single stream, which can be subscribed to as desired. 这意味着给定名称的所有流都将聚合为一个流,可以根据需要订阅该流。
Here's the whole thing working: https://jsbin.com/liwiga/2/edit?js,console,output 这是整个工作的过程: https : //jsbin.com/liwiga/2/edit?js,console,output
One caveat is using flatMapLatest
and merge
with the output of active
like this will not work well if fed cold observables (as each time the array of active observables is emitted, subscriptions begin anew). 一个警告是使用
flatMapLatest
merge
与active
的输出merge
,这样如果喂冷的可观察flatMapLatest
将无法很好地工作(因为每次发出活动的observable数组时,订阅都会重新开始)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.