【问题标题】:Why are chains of IObservables resolved entirely for each subscription?为什么要为每个订阅完全解析 IObservable 链?
【发布时间】:2016-10-31 23:18:29
【问题描述】:

我有一个问题 IObervables 链是如何解决的。 我认为如果多个观察者订阅同一个 IObservable,则整个链只会在流发出新项目时被解析一次。

相反,似乎每个订阅都运行了整个链。

如果我的整个程序逻辑是使用 IObservables 并将它们链接在一起的,那么在我看来,代码正在运行相同的方法并获得相同的结果。

  • 这是有意的行为吗?为什么?

  • 如果是这样,可以避免吗?还是这是个坏主意?

以下代码是用 LINQPad 编写的:

{
    var subject = Observable.Range(1, 1);

    var observable1 = subject
        .Select(value =>
        {
            var nextValue = value + 2;
            Console.Write("\n Result of expensive computation: " + nextValue);
            return nextValue;
        });

    var observable2 = observable1
        .Select(value =>
        {
            var nextValue = 2 * value;
            Console.Write("\n Result of another expensive computation: " + nextValue);
            return nextValue;
        });

    observable2.Subscribe(_ => Console.Write("\n Data received on first subscription."));

    observable2.Subscribe(_ => Console.Write("\n Data received on second subscription."));
}

结果:

昂贵计算的结果:3

另一个昂贵计算的结果:6

第一次订阅时收到的数据。

昂贵计算的结果:3

另一个昂贵计算的结果:6

第二次订阅时收到的数据。

不过我的预期:

昂贵计算的结果:3

另一个昂贵计算的结果:6

第一次订阅时收到的数据。

第二次订阅时收到的数据。

感谢您的任何回答。

干杯, 抢

【问题讨论】:

标签: c# system.reactive


【解决方案1】:

首先回答您的问题:

  • 是的,这是预期的行为。
  • 是的,这是可以避免的。
  • 避免是个好主意。

正如@supertopia 所说,您创建了一个冷可观察对象,其功能与您概述的一样。把它变成一个 hot observable,你就会拥有你想要的功能。

您可以这样做:

{
    var subject = Observable.Range(1, 1);

    var observable1 = subject
        .Select(value =>
        {
            var nextValue = value + 2;
            Console.Write("\n Result of expensive computation: " + nextValue);
            return nextValue;
        });

    var observable2 = observable1
        .Select(value =>
        {
            var nextValue = 2 * value;
            Console.Write("\n Result of another expensive computation: " + nextValue);
            return nextValue;
        });

    var hotObservable = observable2.Publish().RefCount();

    hotObservable.Subscribe(_ => Console.Write("\n Data received on first subscription."));

    hotObservable.Subscribe(_ => Console.Write("\n Data received on second subscription."));

}

冷观测:

  • 多个订阅触发链式操作的多个代码重新运行
  • 通常来自Observable.Create/Range/Generate/Interval/Timer
  • 就像一个可枚举的项目,项目之间有明确定义的时间间隔。

热门观察:

  • 多个订阅一般共享链式操作的结果
  • 通常来自事件、已发布的冷观测值或Subject
  • 比如一系列你可以处理的事件;不管你喜不喜欢,它们都会发生。

在我看来,最大的区别在于源是否在没有订阅的情况下“运行”。例如,一个按钮点击事件,无论是否有人订阅,都会发生。相反,Observable.Timer 在没有订阅的情况下什么都不做。

至于为什么...我不能代表 Rx 设计师说话。 Cold vs hot observables 是混淆/错误的常见来源。我认为在他们周围有更好的明确性可能会改善这种情况。话虽如此,但我不确定这是否可行。

【讨论】:

  • 谢谢。所以 .Publish().RefCount() 通常用于使冷的 observable 变热。但是在您的代码修复中,第二个订阅者永远不会触发。第一个订阅者让 observable 运行它的创建函数并在第二个订阅者订阅之前结束它,对吧?
  • 正确。由于任何可观察对象中都没有异步性,因此一切都是同步运行的。即使您将源增加到var subject = Observable.Range(1, 1000000000); 或其他一些较大的数字,第二个也不会触发。所有一百万个号码将在第一次订阅时同步运行。
  • 如果你想用异步值测试它,使用var subject = Observable.Timer(TimeSpan.FromMilliseconds(10)).Select(_ => 1);。这会导致两个订阅都被触发。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-01
相关资源
最近更新 更多