2

Can someone please explain me why PublishSubject is not working nicely with firstOrError()?

I'm expecting firstOrError to return a NoSuchElementException when the PublishSubject is created without any value.

I wrote some tests in order to better explain the problem:

@Test
fun `test one`() {
    // THIS TEST FAILS
    val publishSubject = PublishSubject.create<Boolean>()

    val testSubscriber = publishSubject
        // .toFlowable(BackpressureStrategy.LATEST) // With or without this doesn't make any difference
        .firstOrError()
        .test()

    testSubscriber
        .assertNotComplete()
        .assertError(NoSuchElementException::class.java)
}

@Test
fun `test two`() {
    // THIS TEST PASSES
    val flowable = Flowable.empty<Boolean>()

    val testSubscriber = flowable
        .firstOrError()
        .test()

    testSubscriber
        .assertNotComplete()
        .assertError(NoSuchElementException::class.java)
}
Filip
  • 19,269
  • 7
  • 51
  • 60
dvdciri
  • 441
  • 6
  • 19
  • "test two" has a timing problem. Because you use `onNext()` before the subscription, you won't see the value `true`. – Bob Dalgleish Jun 22 '18 at 19:37
  • Fair enough, you're right, i'll remove that example cause is just misleading, i mainly care about the empty scenario. What about the other one? – dvdciri Jun 22 '18 at 19:44
  • 1
    In "test one", `publishSubject` never completes, so the `firstOrError()` cannot properly decide anything. – Bob Dalgleish Jun 22 '18 at 19:45
  • Mmm doesn't make much sense.. anyway if that's the case what is the way to achieve the same thing? Get the first item or an error if not there? – dvdciri Jun 22 '18 at 21:41

1 Answers1

3

Short version: Flowable emits no element and completes whereas PublishSubject emits no element and doesn't complete.

Long version:

You assume that PublishSubject.create<Boolean>() is equivalent to Flowable.empty<Boolean>(). But they are not.

First, let's see what firstOrError() really does:

Returns a Single that emits only the very first item emitted by this Observable or signals a NoSuchElementException if this Observable is empty.

So you it makes sense that Flowable.empty<Boolean>() works, because it's, well, empty. What does it mean "empty"?

Returns a Flowable that emits no items to the Subscriber and immediately invokes its onComplete method.

I emphasized important fragment. Flowable.empty() calls onComplete whereas PublishSubject.create() just creates Subject which waits for calling onNext() on him or for subscribers.

So Flowable.empty() is empty, but PublishSubject.create() is not empty. It doesn't call onComplete. See PublishSubject docs for more info.

If you want to have empty PublishSubject, simply call PublishSubject.empty<Boolean>().

michalbrz
  • 3,354
  • 1
  • 30
  • 41
  • 1
    The v2 [PublishSubject](http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/subjects/PublishSubject.html) docs is more elaborate. Besides, `PublishSubject.empty()` is **not** a `PublishSubject` but an `Observable` because `empty()` is a static method on `Observable` visible on all subclasses and can't be overridden. – akarnokd Jun 23 '18 at 12:43
  • 1
    Thanks for detailed explanation of the difference, that really helped. So is it correct to say that the firstOrError() will never work until the PublishSubject calls onComplete or onNext? – dvdciri Jun 23 '18 at 13:11
  • Thanks @akarnokd, your input is always valuable. I update link to docs, it's weird that "default" docs (as you could assume from the link) are for older version. I agree with your point on `empty()` but do you think it makes practical difference or you just wanted to be precise about naming? – michalbrz Jun 23 '18 at 21:45
  • We have canonical links setup in the 1.x javadocs but search engines are not required to follow it when building their indexes. Thus, PublishSubject shows up with the 1.x docs first and 2.x is only the second. There were confusion about such static methods on subjects; some really did expect they return a subject. – akarnokd Jun 23 '18 at 23:04