1

I have an API call which verifies some status against an "Id". The API returns Single or error. I have a list of such Id's, Only one Id is valid to return success or none (all id's return error). What I need is, Iterate through each Id and skip the errors from API call, until either a success or end of the list. I am able to achieve this sequentially. However, I am trying to do the same, using ParallelFlowable. It works fine when an Id returns success, But when there is no id which returns success (all ids fail), then it just skip all the errors from API, but does not notify the subscriber after all the ids are validated. I am not sure how to handle this.

// API call
fun getStatus(Id: String): Single<String> {
  //... returns Single<String> or error
}

//Sequential flow, Working
fun getStatus(ids: List<String>): Single<String> {
  Observable.fromIterable(ids)
                .flatMapSingle { id ->
                    getStatus(id)
                        .onErrorResumeWith { singleSource ->
                            if (ids.last() == id)) { //If this is last item in list, return error
                                singleSource.onError(NoStatusFoundException())
                            } else {
                                // Skip errors until valid id is found or till the list reached end.
                                Flowable.empty<String>()
                            }
                        }
                }.firstOrError()
}

// Parallel Flow, How to identify the list is completed and return NoStatusFoundException in case of all id's fail?
fun getStatus(ids: List<String>): Single<String> { 
              Flowable.fromIterable(ids)
                .parallel()
                .runOn(io())
                .flatMap{ id -> getStatus(id).toFlowable()
                          .onErrorResumeWith { Flowable.empty<String>() }
                        }
                .sequentialDelayError()
                .firstOrError()
                .onErrorResumeNext { Single.error(it) }
}

// Subscription
getStatus(listOf("1","2","3","4","5",))
 .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
 .subscriber({ id->
       // success
       this is notified when an id is success
    },
    { // error handler - Need help here
       Never notified when all the id's fail?
    })

android_user
  • 91
  • 1
  • 4

3 Answers3

1

I am able to resolve this issue by removing onErrorResumeWith { Flowable.empty<String>() } within flatMap and implementing RxJavaPlugins.setErrorHandler{...}. sequentialDelayError() delays all the errors until all the rails have finished their task.

fun getStatus(ids: List<String>): Single<String> { 
              Flowable.fromIterable(ids)
                .parallel()
                .runOn(io())
                .flatMap{ id -> getStatus(id).toFlowable()
                        }
                .sequentialDelayError()
                .firstOrError()
                .onErrorResumeNext { Single.error(it) }
}

///
RxJavaPlugins.setErrorHandler{ it: Throwable ->
  is UndeliverableException -> {...}
  .....
}
android_user
  • 91
  • 1
  • 4
0

You are returning Flowable.empty() that immediately completes the subscription. Taken from the docs:

Returns a Flowable that emits no items to the {@link Subscriber} and immediately invokes its {@link Subscriber#onComplete onComplete} method.

Maybe you can return Flowable.just("") or provide some expected argument incase of an error.

.onErrorResumeWith { Flowable.just("") }
JakeB
  • 2,043
  • 3
  • 12
  • 19
  • I intentionally put `Flowable.empty()` on error inside flatMap, because I wanted to skip the error thrown by `getStatus(id)` call and continue the items from iteration. I wanted to know when fromIterable() has finished emitting all the items, so that subscriber is notified that all id's has failed. – android_user May 08 '20 at 01:07
  • Did you read my post? "emits no items to the Subscriber and immediately invokes its onComplete method." – JakeB May 08 '20 at 07:23
  • I understand, emitting some other value will give me the success with the value I emitted in case of error. that is not what I expect. Infact, Just ignore that error with `Flowable.empty()` and keep calling `getStatus(id)` with other id's in the list. for better understanding I edited my question with subscriber. – android_user May 11 '20 at 22:59
0

The problem is in this line:

.onErrorResumeWith { Flowable.empty<String>() }

The parameter of onErrorResumeWith is a Publisher<out T>, not () -> Publisher<out T>. The Publisher interface happens to have a single method, void subscribe​(Subscriber<? super T> s). As such, it is eligible for SAM conversion.

The lambda { Flowable.empty<String>() } is a perfectly valid (Subscriber<String>) -> Unit that ignores its single parameter, calls a method, and ignores the result. This compiles, but the result is for all practical purposes the same as Flowable.never().

Instead of a lambda, you need to pass Flowable.empty() directly into onErrorResumeNext():

            .flatMap{ id -> getStatus(id).toFlowable()
                      .onErrorResumeWith(Flowable.empty<String>())
                    }
Nitrodon
  • 3,089
  • 1
  • 8
  • 15