Leaving the monad
monad(https://en.wikipedia.org/wiki/Monad_%28functional_programming%29)是一个从函数式编程中比较抽象的一个概念,可能对很多程序员来说并不熟悉。它超出了本指南的范围,在www.introtorx.com中,我们可以找到一个简短的定义:
Monads are a kind of abstract data type constructor that encapsulate program logic instead of data in the domain model.
Monads 是我们感兴趣的,因为 observable 是 Monad。RX代码声明需要做什么,但实际处理不是在执行Rx语句时发生的,而是在发出值时发生的。读者可能会发现阅读更多关于 monads 的内容是很有趣的。对于本指南,当提及Monad时,读者只需考虑 observable。
Why leave the monad
想要离开Monad有两个主要原因,第一个原因是,一个新的Rx开发人员仍然会更习惯于更传统的范例。在不同的范例中执行部分计算可能会使某些部分工作,而您仍在研究如何使用Rx进行操作。第二个原因是,我们通常与没有考虑到Rx的组件和库进行交互。当将现有代码重构为Rx时,让Rx以阻塞方式运行可能是有用的。
BlockingObservable
以分块方式从可观察到的对象中获取数据,第一步是转换为 BlockingObservable.。任何可观察的对象都可以通过以下两种方式转换为 BlockingObservable:您可以使用可观察对象的 toBlocking 方法。
或者BlockingObservable的静态工厂方法
BlockingObservable 没有继承 Observable,也不能与我们通常的Rx操作符一起使用。它有自己的小型函数方法的实现,允许您以阻塞的方式从可观察到的数据中提取数据。这些方法中的许多都是与我们已经看到的方法相对应的阻塞方法。
forEach
Observable 有一个 forEach 方法,forEach 被定义为 subscribe 的别名,主要区别是它不返回 Subscription。来看一个例子
输出:
这里的代码的行为与 subscribe 类似。首先注册一个观察者(forEach接受观察者没有重载,但语义是相同的)。然后执行继续打印“Subscribed”并退出我们的代码段。当发出值(第一个延迟100 ms)时,它们被传递给我们的观察者进行处理。
BlockingObservable 没有 subscribe 方法,但是有 forEach,让我们用 BLockingObservable 改造一下:
输出:
我们在这里看到,对每个对象的调用被阻塞,直到可观察到的任务完成。另一个区别是没有onError和onCompleted的处理程序。onCompleted是在执行完成时给定的,而异常将被抛入运行时进行捕获:
输出:
first, last, single
BlockingObservable 拥有 first ,last 和 single 方法,也实现了firstOrDefault,lastOrDefault和singleOrDefault。在 Observable 中阅读了它们的 namesakes 之后,您已经知道返回的值是什么。同样,不同的是方法的阻塞性质。它们不返回可观察到的值,当值可用时,它就会发出该值。相反,它们会阻塞,直到值可用并返回值本身,而不返回周围可见的值。
输出:
正如我们所看到的,调用首先被阻塞,直到一个值可用,然后才返回一个值。
与forEach一样,异常也会在运行时中抛出以供捕获。
输出:
To Iterable
您可以通过BlockingViewable上的各种方法将您的可观察性转换为可迭代性。Iterable是基于拉的,与rx不同的是,rx是基于推的。这意味着,当使用者准备使用一个值时,会在可迭代的Iterator上使用Next()请求一个值。对Next()的调用要么立即返回一个值,要么阻塞,直到一个值准备好为止。
有几种方法可以从BlockingViewable<T>到Iterable<T>,并且每种方法都有不同的行为。
在此实现中,将收集和缓存所有发出的值。由于缓存,不会漏掉任何项目。迭代器将尽快获取下一个值,如果下一个值已经发生,则立即获取,或者阻塞,直到下一个值可用为止。
输出:
值得注意的是,在下一个通知可用之前,应该使用hasNext()或Next()块。如果完成,hasNext返回false,next抛出java.util.NoSuchElementException
next
在这个实现中,根本不缓存值。迭代器将始终等待下一个值并返回该值。
输出:
在本例中,使用者比生产者慢,并且总是错过下一个值。迭代器将获得下一个迭代器。
latest
latest 方法类似于Next,不同之处在于它将缓存一个值。迭代器只会在自上一个值被使用后可观察到的事件没有发出时阻塞。只要有新的事件发生,迭代器就会立即返回一个值,或者随着迭代的结束而返回。
输出:
使用 latest 迭代器时,如果在发出下一个事件之前没有提取值,则会跳过这些值。如果使用者比生产者快,迭代器将阻塞并等待下一个值。
有趣的是,4从未被消耗掉。那是因为一个onCompleted紧随其后,导致下一个拉看到一个终止的可观察到的。隐式iterator.hasNext()方法报告终止的可观察值,而不检查是否使用了最后一个值。
mostRecent
mostRecent 迭代器从不阻塞。它缓存这个值,因此,如果使用者速度慢,则可以跳过值。与最新版本不同,最后一个缓存的值总是返回,如果使用者的速度快于生产者,就会导致重复。为了使最近的迭代器完全无阻塞,需要一个初始值.。如果可观察到的对象尚未发出任何值,则返回该值。
输出:
Future
BlockingObservable<T>可以用 toFuture 方法表示为 Future 。此方法仅创建Future的实例,而不阻止。获取值时根据需要执行块。Future 允许使用者决定如何处理异步操作。Future 还能够报告操作中的错误
输出:
以这种方式创建的Future 希望observable 发出单个值,就像单个方法所做的那样。如果发出多个项,Future将报告java.lang.IllegalArgumentException。
Locks
Deadlocks
到目前为止,我们能够忽略潜在的死锁。Rx的非阻塞性使得创建不必要的死锁变得更加困难。然而,在本章中,我们又回到了阻塞方法,从而将死锁再次提到了最前沿。
下面的例子可以作为一个非阻塞案例。但是因为我们使用了阻塞操作,所以它永远不会解除阻塞。
输出:
forEach仅在序列终止后返回。但是,终止事件要求Each在被推送之前返回。因此,每个人都将永远不会解除封锁。
Non-terminating sequences
访问可观测数据的一些阻塞方法,如 last()
,要求被观测到的对象终止并解除阻塞。其他的,比如first(),要求它至少发出一个事件来解除阻塞。将这些方法用于可观察性并不是很大的危险,因为它们只返回一个不可终止的可观察性.。如果使用者没有花时间强制执行一些保证,例如超时,BlockingViewable上的这些相同的方法可能会导致一个永久的块(我们将看到在[Timeshifter sequences](/Part 3 - Taming the sequence/5. Time-shifted sequences.md)。
原文链接:
https://github.com/Froussios/Intro-To-RxJava/blob/master/Part%203%20-%20Taming%20the%20sequence/2.%20Leaving%20the%20monad.md