Rx:什么是订阅,订阅如何工作?

时间:2018-08-22 07:22:43

标签: c# .net rxjs reactive-programming system.reactive

我正在学习.NET中的反应式扩展(rx),并且在“预订”的真正含义和使用时间方面有些挣扎。

让我们从this线程中获取一些样本数据:

using System;
using System.Reactive.Linq;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        class Result
        {
            public bool Flag { get; set; }
            public string Text { get; set; }
        }

        static void Main(string[] args)
        {               
            var source =
               Observable.Create<Result>(f =>
               {
                   Console.WriteLine("Start creating data!");

                   f.OnNext(new Result() { Text = "one", Flag = false });
                   Thread.Sleep(1000);
                   f.OnNext(new Result() { Text = "two", Flag = true });
                   Thread.Sleep(1000);
                   f.OnNext(new Result() { Text = "three", Flag = false });
                   Thread.Sleep(1000);
                   f.OnNext(new Result() { Text = "four", Flag = false });
                   Thread.Sleep(1000);
                   f.OnNext(new Result() { Text = "five", Flag = true });
                   Thread.Sleep(1000);
                   f.OnNext(new Result() { Text = "six", Flag = true });
                   Thread.Sleep(1000);
                   f.OnNext(new Result() { Text = "seven", Flag = true });
                   Thread.Sleep(1000);
                   f.OnNext(new Result() { Text = "eight", Flag = false });
                   Thread.Sleep(1000);
                   f.OnNext(new Result() { Text = "nine", Flag = true });
                   Thread.Sleep(1000);
                   f.OnNext(new Result() { Text = "ten", Flag = false });

                   return () => Console.WriteLine("Observer has unsubscribed");
               });
        }
    }
}

当心这一行:

 Console.WriteLine("Start creating data!");

现在,首先,我认为只需使用.Subscribe运算符即可使用订阅。因此,观察者(例如.Subscribe函数的回调)订阅了这样的可观察对象(操作符链的最后一个返回值)(仅作为示例,查询没有实际用途):

  source.Zip(source, (s1, s0) =>
     s0.Flag
     ? Observable.Return(s1)
     : Observable.Empty<Result>()).Merge().Subscribe(f => { Console.WriteLine(f.Text); });

现在我期望得到“开始创建数据!”仅输出一次,因为我只使用一个订阅。但实际上,我两次获得了

Start creating data!
Start creating data!
two
five
six
seven
nine

有人告诉我,每次在source.上使用运算符时,都会进行订阅。但是在此示例中,我仅使用source.一次,然后再次使用.Zip作为操作符的参数。还是因为通过再次订阅的值将源传递到.Zip函数?

所以我的问题是:

  1. 就Rx而言,“订阅”到底是什么?
  2. 在我的示例中何处/为什么发生这两个订阅?

顺便说一句。我知道我可以使用.Publish运算符来防止发生多个订阅,但这不是我的问题的范围。

1 个答案:

答案 0 :(得分:2)

简单来说,“订阅”仅代表已订阅的Observable。可以通过使用.Subscribe显式地进行此过程,也可以通过加入两个或多个Observables并随后订阅所生成的链来隐式地进行该过程。

在您的情况下,您会看到两种情况都发生,一次是在调用Subscribe时显式发生,一次是在将source传递到Zip时隐性发生,即有两个{{1} }到Subscriptions source

为什么那么重要?因为默认情况下Observable是惰性的,这意味着它们只有在您订阅它们后才会开始处理(该过程的产品为Observables),从广义上讲,这意味着 any 订阅Subscription时,它将有效地开始新的视频流。可以像您用Observable所提到的那样来覆盖此行为,但是默认情况下,每个Publish都要 cold

在您的特定情况下,由于您要将相同的Observable传递给Observable,因此需要订阅两次,因为它将把来自两个传递的流的事件压缩在一起。结果是对同一Zip的两个订阅,每个订阅彼此独立运行。