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

时间:2015-02-03 02:01:07

标签: java rx-java

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

它已经完成并且有效。

但我想知道......

  1. 这是一个很好的Rx风格吗?
  2. 如果不好,请注明要点
  3. 以下是我处理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));
    

    编辑

    我快要换了。 thanks for good comment.
    (但我有一些未替换的代码。它有很多回调和if语句。)

    我想避免'回调地狱'。

    密钥是'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()));
    
    抱歉,我的英语不好,问题很长。

    更新了我的代码

    我在这个问题上得到了两个重要信息。(谢谢你@TomášDvořák,@ Will

    1. 这是否是一个好的方式取决于具体情况。
    2. 在map / flatmap / subscribe中使用if语句没有错。
    3. 更新代码

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

2 个答案:

答案 0 :(得分:13)

有很多方法可以做到这一点,这取决于你的用例。一般来说,我不想分成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的方法并在那里执行我的逻辑时,我只使用flatMapObservable

<强>更新

仍然不确定为什么要拆分你的溪流。除非你开始使用不同的线程聪明,否则第一个订阅调用将阻止第二个调用,这可能不是你想要的。此外,如果您不多次拨打订阅,那么您就不需要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电话感到有些困惑。这是用于调试还是记录目的?如果是这样,只需在if语句中的上述flatMap中执行此操作。

希望这有帮助,

威尔

答案 1 :(得分:1)

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

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