Как заменить "if statement" на rx-java, чтобы избежать обратного вызова ад?
Я пытаюсь заменить мой код на rx-java. (Это очень маленький код.)
Он закончен, и он работает.
Но я хочу знать...
- Это хороший стиль Rx?
- Если не хорошо, укажите плохую точку.
Ниже мой код, который обрабатывает api.
перед
Random r = new Random();
boolean apiResult = r.nextBoolean(); // it represents api result. ex. {"result": true} or {"result": false}
if (apiResult == true) {
// do something
System.out.println("result:" + "success");
} else {
// do something
System.out.println("result:" + "failure");
}
после
Random r = new Random();
Observable<Boolean> apiResultStream = Observable.create(new OnSubscribe<Boolean>() {
@Override
public void call(Subscriber<? super Boolean> subscriber) {
// emit true or false
subscriber.onNext(r.nextBoolean());
}
}).cache(1);
// I used filter for split. Is it Rx style?
// success if true emitted.
Observable<Boolean> successStream = apiResultStream
.filter(aBoolean -> aBoolean == true); // here
// failure if false emitted.
Observable<Boolean> failureStream = apiResultStream
.filter(aBoolean -> aBoolean == false); // here
// success flow
successStream
.flatMap(aBoolean -> Observable.just("success"))
// and do something
.subscribe(aString -> System.out.println("result:" + aString));
// failure flow
failureStream
.flatMap(aBoolean -> Observable.just("failure"))
// and do something.
// I want to keep subscriber.
.subscribe(aString -> System.out.println("result:" + aString));
EDIT
Я почти заменил. спасибо за хороший комментарий.
(но у меня есть несколько не замененных кодов. У него много запросов обратного вызова и if).
Я хочу избежать "callback hell".
Ключ - это другой тип результата между 'callSuccessApi' и 'callFailureApi'
до rx
// callback hell!
callApi(new Callback<Result>(){
@Override
public void success(Result result) {
if (result.Response == true) {
callSuccessApi(new Callback<ResultSuccess>(){
@Override
public void success(ResultSuccess result) {
// and more callbacks...
}
}
} else { // result.Response == false
callFailureApi(new Callback<ResultFailure>(){
@Override
public void success(ResultFailure result) {
// and more callbacks...
}
}
}
}
}
после с rx (избегать обратного вызова ад! Это хороший стиль Rx?)
// change 1st api to observable.(I changed other api to observable)
Observable<Result> apiResultStream = Observable.create(new OnSubscribe<Boolean>() {
@Override
public void call(Subscriber<? super Boolean> subscriber) {
callApi(new Callback<Result>(){
@Override
public void success(Result result) {
subscriber.onNext(result);
}
});
}
}).cache(1); // ensure same Observable<Result> for success and failure.
// I used filter for split. Is it Rx style?
// success if result.response == true.
Observable<ResultSuccess> successStream = apiResultStream
.filter(result -> result.response == true); // here
// failure if result.response == false.
Observable<ResultFailure> failureStream = apiResultStream
.filter(result -> result.response == false); // here
// success flow. callSuccessApi return Observable<ResultSuccess>
successStream
.flatMap(result -> callSuccessApi(result))
// and more api call with flatMap...
.subscribe(resultSuccessN -> System.out.println("result:" + resultSuccessN.toString()));
// failure flow. callFailureApi return Observable<ResultFailure>
failureStream
.flatMap(resultFailure -> callFailureApi(result))
// and more api call with flatMap...
.subscribe(resultFailureN -> System.out.println("result:" + resultFailureN.toString()));
Извините за мой бедный английский и длинный вопрос.
Обновлен мой код
У меня есть 2 важных информации в этом вопросе (спасибо @Tomáš Dvořák, @Will
- будет ли это хороший способ, зависит от конкретной ситуации.
- Нет ничего плохого в использовании инструкции if в map/flatmap/subscribe.
обновленный код
Observable<Result> apiResultStream = Observable.create(new OnSubscribe<Boolean>() {
@Override
public void call(Subscriber<? super Boolean> subscriber) {
callApi(new Callback<Result>() {
@Override
public void success(Result result) {
subscriber.onNext(result);
}
});
}
});
// In this case, I used 'if' for simply and cleanly.
apiResultStream
.subscribe(result -> {
if (result.response == true) {
callSuccessApi(); // this line looks like 'callback'. but I used this for simply and cleanly.
} else {
callFailureApi();
}
});
Ответы
Ответ 1
Существует множество способов сделать это, и это действительно зависит от вашего варианта использования. В общем, я бы не хотел разбивать на 2 потока, так как это делает ваш код менее удобочитаемым. Кроме того, я не уверен, какую выгоду вы получите от звонка flatMap. Нет ничего плохого в том, чтобы делать, если вещи в карточном вызове.
Вот несколько вариантов:
1 - для добавления журнала (немного похоже на ваши строки печати) я использую doOnEach()
apiResultStream
.doOnEach(next -> {
if (next) logger.info("Logging true " + next);
else logger.info(Logging false " + next);
})
.subscribe(....
2 - Работа, которую вы выполняете, является частью вашего потока, и вы захотите больше работать над потоком позже - используйте map
apiResultStream
.map(next -> {
if (next) doSomeCallWithNextWhenTrue(next);
else doSomeCallwithNextWhenFalse(next);
})
.subscribe(...
3 - Если это работа, которую вы хотите выполнить в конце конвейера - IE после того, как все трансформационные или другие потоки, например, работа завершена, сделайте это в вызове подписки.
apiResultStream
.subscribe(next -> {
if (next) doSomeCallWithNextWhenTrue(next);
else doSomeCallwithNextWhenFalse(next);
});
Проблема - с таким простым прецедентом трудно предложить лучший вариант, но я понимаю, что в обучении Rx разработка способов выполнения условных операторов может показаться запутанной. В общем, я просто использую map
или flatMap
, когда я вызываю другой метод, который возвращает Observable
и выполняет мою логику там.
Обновление
Не уверен, почему вы раскалываете свои потоки. Если вы не начнете разбираться в разных потоках, первый вызов подписки будет блокировать второй, который, вероятно, не тот, который вы хотите. Кроме того, если вы не вызываете подписку более одного раза, вам не нужен вызов cache()
.
Нет ничего плохого в использовании if statement
в map
/flatMap
/subscribe
. Особенно, если это делает ваш код более читаемым.
Я бы сделал следующее:
apiResultStream
.flatMap(result -> {
if (result.response == true) {
return callSuccessApi(result)
}
else {
return callFailureApi(result)
})
//Do any more calls you need
.subscribe(...
Так много чище.
Я немного смущен вашими вызовами System.out.println
в подписке. Это для отладки или ведения журнала? Если это так, просто сделайте это в указанной выше flatMap в инструкции if.
Надеюсь, что это поможет,
Воля
Ответ 2
Чтобы избежать if/else и не разрывать цепочку ™, мне нравится использовать publish и merge для разделения и повторного объединения потока:
apiResultStream
.publish(results ->
Observable.merge(
results.filter(result -> result.response == true)
.flatmap(result -> callSuccessApiObservable()),
results.filter(result -> result.response == false)
.flatmap(result -> callFailureApiObservable())
)
)
.subscribe(...