那么,我们如何使我们的Observable具有惰性呢?最简单的技术是用defer()来包装一个eager 的Observable。

public Observable<Person> listPeople() {
    return Observable.defer(() ->
        Observable.from(query("SELECT * FROM PEOPLE")));
}

defer()接收一个以生成Observable的lambda表达式(一个工厂)。底层的Observable是eager的,所以我们想推迟它的创建。defer()将等待直到最后一刻才真正创建Observable;也就是说,直到有人真正订阅了它。这有一些有趣的暗示。因为Observable是惰性的,因此调用listPeople()没有副作用,而且几乎没有性能占用。还没有查询数据库呢。您可以将Observable<Person>视为一个承诺,但没有任何后台处理。请注意,目前没有异步行为,只是延迟评估。这类似于Haskell编程语言中的值仅在绝对需要时才被延迟评估。

如果你从未用函数式语言编程,你可能会很困惑为什么惰性如此重要和具有开创性。事实证明,这种行为非常有用,可以大大提高实现的质量和自由。例如,您不再需要注意获取、何时、以什么顺序获取资源。RxJava只在需要时才加载它们。

举个例子,我们已经看到过很多次的这种简单的回退机制:

void bestBookFor(Person person) {
    Book book;
    try {
        book = recommend(person);
    } catch (Exception e) {
        book = bestSeller();
    }
    display(book.getTitle());
}
void display(String title) {
    //...
}

你可能认为这样的结构没有什么错。在这个例子中,我们试图为一个特定的人推荐最好的书(recommend),但是万一失败,我们会优雅地降级并显示出畅销书(bestSeller)。假定获取畅销书(bestSeller)的速度更快,而且可以缓存。但是,如果您可以以声明方式添加错误处理,以便try - catch块不会模糊真实逻辑,那这该怎么办呢?

void bestBookFor(Person person) {
    Observable<Book> recommended = recommend(person);
    Observable<Book> bestSeller = bestSeller();
    Observable<Book> book = recommended.onErrorResumeNext(bestSeller);
    Observable<String> title = book.map(Book::getTitle);
    title.subscribe(this::display);
}

到目前为止,我们只研究RxJava,因此我保留了所有这些中间值和类型。在实际生产中,bestBookFor()看起来更像这样:

void bestBookFor(Person person) {
    recommend(person)
        .onErrorResumeNext(bestSeller())
        .map(Book::getTitle)
        .subscribe(this::display);
}

这段代码简洁而易读。首先为某人找到一个推荐(即上文说的针对某个特定的人所作出的推荐好书)。如果出现错误(onErrorResumeNext),那么继续使用畅销书降级代替(bestseller)。无论哪一个成功,map通过提取title来返回值,并显示它。onErrorResumeNext()是一个强大的操作符,它拦截了上游的异常,并吞下它们,并订阅了提供的备份的Observable。这就是Rx如何实现try - catch子句的。在本书的247页的“Declarative try-catch Replacement”上,我们将花更多的时间在错误上处理上。就目前而言,请注意我们如何可以惰性地调用bestSeller(),而不必担心获取畅销书的发生,尤其是当获取读者推荐成功时候。

results matching ""

    No results matching ""