34

I'm trying to get the latest value of a given Observable and get it to emit immediately once it's called. Given the code below as an example:

return Observable.just(myObservable.last())
    .flatMap(myObservable1 -> {
        return myObservable1;
    })
    .map(o -> o.x) // Here I want to end up with a T object instead of Observable<T> object

This does not work because by doing this the flatMap will emit myObservable1 which in turn will have to emit to reach the map. I don't know if doing such a thing is even possible. Does anyone have any clue on how to achieve this goal? Thank you

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
E-Kami
  • 2,529
  • 5
  • 30
  • 50
  • What observable are you talking about? What do you mean by latest? – akarnokd May 03 '16 at 16:23
  • `myObservable` is a hot observable which emit for example: "1", "2", "3" at irregular intervals. What I want to do is being able to get `myObservable` latest value ("3" in our case) at the time I reach the `return` directive – E-Kami May 03 '16 at 16:30
  • Is myObservable hot or cold? – yevt May 04 '16 at 05:33
  • 1
    Seems like you you are still thinking in imperative(not reactional) way. Could you give a bigger perspective? Point is that normally, you create observables based on other observables. You can create lastObservable based on myObservable, which will be blocked until myObservable ends, and then emit last value. There's no way out of these stream "tubes" except endpoints like subscribe. – yevt May 04 '16 at 05:45
  • 1
    I once asked a similar question on the RxJava Google Group - maybe Ben's answers there are helpful to you: https://groups.google.com/forum/#!searchin/rxjava/mihola/rxjava/I4CxPoU5rpE/v44DIj3Q2RAJ – david.mihola May 04 '16 at 06:42
  • ```startsWith``` for the default value and ```replay(1)``` + ```connect``` don't do the trick? – Vincent D. Nov 01 '16 at 21:15

3 Answers3

36

last() method will not be of any help here as it waits for the Observable to terminate to give you the last item emitted.

Assuming that you do not have the control over the emitting observable you could simply create a BehaviorSubject and subscribe it to the observable that emits the data that you want to listen and then subscribe to the created subject. Since Subject is both Observable and Subscriber you will get what you want.

I think (do not have the time to check it now) you may have to manually unsubscribe from the original observable as the BehaviorSubject once all of his subscribers unsubscribe will not unsubscribe automatically.

Something like this:

BehaviorSubject subject = new BehaviorSubject();
hotObservable.subscribe(subject);
subject.subscribe(thing -> {
    // Here just after subscribing 
    // you will receive the last emitted item, if there was any.
    // You can also always supply the first item to the behavior subject
});

http://reactivex.io/RxJava/javadoc/rx/subjects/BehaviorSubject.html

MatBos
  • 2,380
  • 24
  • 34
3

In RxJava, subscriber.onXXX is called asynchronous.It means that if your Observable emit items in new thread, you can never get the last item before return, except you block the thread and wait for the item.But if the Observable emit item synchronously and you dont' change it's thread by subscribeOn and observOn, such as the code:

Observable.just(1,2,3).subscribe();

In this case, you can get the last item by doing like this:

Integer getLast(Observable<Integer> o){
    final int[] ret = new int[1];
    Observable.last().subscribe(i -> ret[0] = i);
    return ret[0];
}

It's a bad idea doing like this.RxJava prefer you to do asynchronous work by it.

dieyidezui
  • 51
  • 3
1

What you actually want to achieve here is to take an asynchronous task and transform it to a synchronous one.

There are several ways to achieve it, each one with it's pros and cons:

  • Use toBlocking() - it means that this thread will be BLOCKED, until the stream is finish, in order to get only one item simply use first() as it will complete once an item is delivered. let's say your entire stream is Observable<T> getData(); then a method that will get the last value immediately will look like this:

public T getLastItem(){ return getData().toBlocking().first(); }

please don't use last() as it will wait for the stream to complete and only then will emit the last item.

If your stream is a network request and it didn't get any item yet this will block your thread!, so only use it when you are sure that there is an item available immediately (or if you really want a block...)

  • another option is to simply cache the last result, something like this:

    getData().subscribe(t-> cachedT = t;) //somewhere in the code and it will keep saving the last item delivered public T getLastItem(){ return cachedT; }

if there wasn't any item sent by the time you request it you will get null or whatever initial value you have set. the problem with this approch is that the subscribe phase might happen after the get and might make a race condition if used in 2 different threads.

ndori
  • 1,934
  • 1
  • 18
  • 23
  • I'm having trouble following the code here; what class is getLastItem applied to and what are the details of the getData() function? – TahoeWolverine Sep 19 '19 at 22:38
  • GetData is just to emphasize a stream you are using. You can replace it with the observable you are using, let's say myObservable in the original example Observable myObservable; Then myObservable.toBlocking().first() Will give you the first item once it arrives. – ndori Sep 21 '19 at 06:07