简体   繁体   English

如何在RxJS中不带主题的情况下直接在发布子系统中链接生产者和消费者?

[英]How can I directly link producers and consumers in a pub sub system, without subjects, in RxJS?

Edit 编辑

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! 谢谢你们每一个人的帮助!

Original 原版的

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: 结果应该是流:

  • Never completes; 永远不会完成;
  • aggregates the results of all active streams of a certain name and discards those that are no longer active; 聚合某个名称的所有活动流的结果,并丢弃不再活动的结果;
  • lazily updates all consumers who have received a reference to that stream, such that if a new stream by the same name is published, the consumer's reference remains valid and is re-evaluated to merge in that new stream; 懒惰地更新已收到对该流的引用的所有使用者,这样,如果发布了一个具有相同名称的新流,则该消费者的引用仍然有效,并被重新评估以合并到该新流中;
  • caches the last merged result, such that new consumers will receive the last emitted value on subscription. 缓存最后合并的结果,以便新使用者在订阅时将收到最后发出的值。

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!

I also have a JSBin of this here . 在这里也有一个JSBin。

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的流数组,我们随后使用flatMapLatestmerge其扁平化为单个流。 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 mergeactive的输出merge ,这样如果喂冷的可观察flatMapLatest将无法很好地工作(因为每次发出活动的observable数组时,订阅都会重新开始)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 RXJS 在 React 中没有主题 - RXJS in React without Subjects 如何使此发布/子代码更具可读性? - How can I make this pub/sub code more readable? 如何使用 QUnit(或其他 js 单元测试框架)对 pub/sub 进行单元测试 - How can I unit test pub/sub using QUnit (or another js unit testing framework) RequireJS,发布/订阅。 没有坏的解决方法,我无法从其自己的方法访问视图实例。 - RequireJS, Pub/Sub. I can't access view instance from its own method without a bad workaround. 我如何获得状态的最后一个值? 无需订阅(rxJs) - How i can get last value of state? without subscribing (rxJs) 如何在不使用已弃用的 retryWhen 的情况下延迟 RxJs 重试? - How can I retry with a delay in RxJs without using the deprecated retryWhen? 如何从主题创建Observable而不将主题暴露给RxJs v5中的使用者 - How to create Observable from Subject without exposing the Subject to consumers in RxJs v5 如何直接链接到特定的JavaScript选项卡? - How can I link directly to a specific JavaScript tab? 如何让不同的Web浏览器在Redis / Node.js / Socket.io pub / sub设置中订阅单独的频道? - How can I have different web browsers subscribe to separate channels in a Redis/Node.js/Socket.io pub/sub setup? 是否可以直接通过App Engine Cron触发发布/订阅事件 - Is it possible to trigger Pub/Sub event with App Engine Cron directly
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM