如何用 rx-java 替换“if 语句”以避免回调地狱?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28290163/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 13:20:36  来源:igfitidea点击:

How to replace 'if statement' with rx-java for avoiding callback hell?

javarx-java

提问by kyanro

I am trying to replace my code with rx-java. (It is very small code.)

我正在尝试用 rx-java 替换我的代码。(这是非常小的代码。)

It is finished and it works.

它已经完成并且可以工作了。

But I want to know...

但是我想知道...

  1. Is it a good Rx style?
  2. If not good, please indicate bad point
  1. 这是一个好的 Rx 风格吗?
  2. 如果不好,请指出不好的点

Below is my code that is api handling.

下面是我的 api 处理代码。

before

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");
}

after

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

编辑

I almost replaced. thanks for good comment.
(but I have a few non-replaced code. It have many callback and if statement.)

我差点换了。谢谢你的好评论。
(但我有一些不可替换的代码。它有很多回调和 if 语句。)

I want to avoid 'callback hell'.

我想避免“回调地狱”。

The key is different result type between 'callSuccessApi' and 'callFailureApi'

关键是“callSuccessApi”和“callFailureApi”之间的结果类型不同

before 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...
                }
            }
        }
    }
}

after with rx(avoid callback hell! Is it a good Rx style?)

after with 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()));

sorry for my poor English and long question.

对不起,我的英语很差,问题很长。

Updated My Code

更新了我的代码

I got 2 important information in this question.(thank you @Tomá? Dvo?ák, @Will

我在这个问题中得到了 2 个重要信息。(谢谢@Tomá?Dvo?ák,@Will

  1. whether it is a good way to go depends on the particular situation.
  2. There's nothing wrong with using an if statement within an map / flatmap / subscribe.
  1. 这是否是一条好路取决于具体情况。
  2. 在 map/flatmap/subscribe 中使用 if 语句并没有错。

updated code

更新代码

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();
                }
            });

采纳答案by Will

There are loads of ways of doing this and it really depends on your use case. In general I wouldn't want to split into 2 streams, as that makes your code less readable. Also, I'm not sure what benefit you get from the flatMap call. There's nothing wrong with doing if stuff within a map call.

有很多方法可以做到这一点,这实际上取决于您的用例。一般来说,我不想分成 2 个流,因为这会降低您的代码的可读性。另外,我不确定您从 flatMap 调用中获得什么好处。在地图调用中执行 if 内容并没有错。

Here are a few options:

这里有几个选项:

1 - For adding logging (a bit like your print lines), I use doOnEach()

1 - 为了添加日志(有点像你的打印行),我使用 doOnEach()

apiResultStream
  .doOnEach(next -> {
    if (next) logger.info("Logging true " + next);
    else  logger.info(Logging false " + next);
  })
  .subscribe(....

2 - The work you're doing is part of your stream, and you're going to want to do more work on the stream later - use map

2 - 你正在做的工作是你的流的一部分,你以后会想要在流上做更多的工作 - 使用 map

apiResultStream
  .map(next -> {
        if (next) doSomeCallWithNextWhenTrue(next);
        else doSomeCallwithNextWhenFalse(next);
      })
  .subscribe(...

3 - If this is work you want to do at the end of the pipeline - IE after all transformational or other stream like work has completed, then do it in the subscribe call.

3 - 如果这是您想在管道结束时完成的工作 - IE 在所有转换或其他流等工作完成后,请在 subscribe 调用中进行。

apiResultStream
  .subscribe(next -> {
            if (next) doSomeCallWithNextWhenTrue(next);
            else doSomeCallwithNextWhenFalse(next);
          });

The problem is - with such a simple use case, it's difficult to suggest the best option, but I appreciate that in learning Rx, working out how to do conditional statements can seem confusing. In general, I just use mapor flatMapwhen I'm calling another method that returns an Observableand do my logic in there.

问题是 - 对于如此简单的用例,很难提出最佳选择,但我很欣赏在学习 Rx 时,弄清楚如何执行条件语句似乎令人困惑。一般来说,我只是使用mapflatMap当我调用另一个返回 an 的方法Observable并在那里执行我的逻辑时。

Update

更新

Still not sure why you're splitting your streams. Unless you start getting clever with different threads, the first subscribe call is going to block the second which is probably not what you want. Also, if you don't call subscribe more than once, then you don't need the cache()call.

仍然不确定为什么要拆分流。除非您开始巧妙地使用不同的线程,否则第一个 subscribe 调用将阻塞第二个,这可能不是您想要的。此外,如果您不多次调用 subscribe,则不需要cache()调用。

There's nothing wrong with using an if statementwithin an map/ flatmap/ subscribe. Especially if it makes your code more readable.

有什么不妥使用if statementmap/ flatmap/ subscribe。特别是如果它使您的代码更具可读性。

I would do the following:

我会做以下事情:

apiResultStream
  .flatMap(result -> {
    if (result.response == true) {
      return callSuccessApi(result)
    }
    else {
      return callFailureApi(result)
  })
  //Do any more calls you need
  .subscribe(...

So much cleaner.

干净多了。

I'm a bit confused by your System.out.printlncalls in subscribe. Is this there for debug or logging purposes? If so, just do that within the above flatMap in the if statement.

我对您System.out.println的订阅电话感到有些困惑。这是用于调试或记录目的吗?如果是这样,只需在 if 语句中的上述 flatMap 中执行此操作。

Hope this helps,

希望这可以帮助,

Will

将要

回答by Till

To avoid the if/else and to not break the chain?, I like to use publish and merge to split and re-merge the stream:

为了避免 if/else 并且不破坏链?,我喜欢使用发布和合并来拆分和重新合并流:

apiResultStream
  .publish(results -> 
    Observable.merge(
        results.filter(result -> result.response == true)
               .flatmap(result -> callSuccessApiObservable()),
        results.filter(result -> result.response == false)
               .flatmap(result -> callFailureApiObservable())
    )
  )
  .subscribe(...