在了解Rx时,我遇到了The Observable Contract中详述的 Observables 经常重复的规则。
发出OnCompleted或OnError通知后,此后可能不再发出任何通知。
这对我来说很有意义,因为让Observable在完成后继续生成值会让人感到困惑,但是当我在.NET中测试Observable.Range方法时,我注意到它没有表现出这种行为,实际上许多 Observable违反了这条规则。
System.out.println("C:\\Users\\Julian Jacobs\\Pictures\\Saved Pictures\\spaceship\\spaceship.png")
显然var rangeObservable = Observable.Range(0, 5);
rangeObservable.Subscribe(Console.WriteLine, () => Console.WriteLine("Done first!"));
Console.ReadLine();
rangeObservable.Subscribe(Console.WriteLine, () => Console.WriteLine("Done second!"));
Console.ReadLine();
//Output:
//0
//1
//2
//3
//4
//Done first!
//0
//1
//2
//3
//4
//Done second!
已拨打rangeObservable
两次,并在第一个OnComplete
之后生成了值。这让我相信这不是关于 Observables 的规则,而是关于订阅的规则。也就是说,只要每个订阅只收到一个终止消息并且没有之后的更多信息。
当它们显示 Observable 时,它们实际上是指订阅吗?他们真的是不同的东西吗?我对该模型有一个基本的误解吗?
答案 0 :(得分:4)
可观察合约必须对任何观察的Observable有效。 在Observable未被观察时是否发生任何事情都留给了observable的实现。
在Enumerable中考虑模拟是有帮助的 - Observable是Enumerable的对偶。在枚举中,你会有
range = Enumerable.Range(0, 5)
,您可以使用与上述类似的范围:
range.ForEach(Console.WriteLine); //prints 0 - 4
range.ForEach(Console.WriteLine); //prints 0 - 4 again
并发现这是完全可以接受的行为,因为只有在调用GetEnumerator
时才会创建实际的数字生成器。同样,在Observable中,等效方法是Subscribe
。
范围的实现类似于:
static IObservable<int> Range(int start, int count)
{
return Observable.Create<int>(observer =>
{
for (int i = 0; i < count; i++)
observer.OnNext(start + i);
observer.OnCompleted();
return Disposable.Empty;
});
}
这里,每次订阅时都会调用observer => {...}
函数。工作在订阅方法中完成。您可以很容易地看到它(1)为每个观察者推送相同的序列,(2)每个观察者只完成一次。
这些可观察事物只有在你观察它们时才会发生,它们被称为冷可观察物。 Here's an article描述了这个概念。
注意强>
Range
是一个非常天真的实现,仅用于说明目的。该方法在完成之前不会返回,因此Disposable.Empty
是可接受的。正确的实现将在调度程序上运行工作并使用已检查的一次性来查看订阅是否已在继续循环之前处置。
需要注意的是,手动实现可观察合约是 hard ,这就是Rx库存在的原因 - 通过合成来构建功能。
答案 1 :(得分:4)
Observable.Range
返回 cold observable,这意味着它“重播”每个订阅者的行为。由于“OnNext * OnComplete | OnError”合约仅适用于订阅,因此完全没问题。
有关热/冷可观测量的更多信息,请参阅my answer on "IConnectableObservables in Rx"