如何在RxJava中同步异步方法? RxJava中的异步瀑布

时间:2017-03-14 15:13:00

标签: java android asynchronous rx-java async.js

使用RxJava,如何在数据列表上依次执行异步方法?

使用Node.js Async 模块,可以按以下方式顺序调用对象数组上的函数:

var dataArray = ['file1','file2','file3']; // array of arguments
var processor = function(filePath, callback) { // function to call on dataArray's arguments
    fs.access(filePath, function(err) { // perform an async operation
        // process next item in dataArray only after the following line is called
        callback(null, !err)
    }
};
async.every(dataArray, processor, function(err, result) {
    // process results
});

这样做的好处是processor函数中执行的代码可以是异步的,callback只能在异步任务完成后运行。这意味着dataArray中的每个对象将一个接一个地处理,而不是并行处理。

现在,在RxJava中,我可以通过调用:

来调用dataArray上的处理函数
String[] dataArray = new String[] {"file1", "file2", "file3"};
Observable.fromArray(dataArray).subscribe(new Consumer<String>() { // in RxJava 1 it's Action1
    @Override
    public void accept(@NonNull String filePath) throws Exception {
        //perform operation on filePath
    }
});

但是,如果我在filePath上执行异步操作,我如何确保对dataArray项的顺序执行?我正在寻找的是:

String[] dataArray = new String[] {"file1", "file2", "file3"};
Observable.fromArray(dataArray).subscribe(new Consumer<String>() {
     // process next item in dataArray only after the callback is called
    @Override
    public void accept(@NonNull String filePath, CallNext callback) throws Exception {
         // SomeDatabase will call callback once
         // the asynchronous someAsyncOperation finishes
         SomeDatabase.someAsyncOperation(filePath, callback);
    }
});

此外,如果dataArray的所有项目都已处理,我该如何调用某些代码?一个'完成听众'的排序?

注意:我意识到我可能错误地接受了RX概念。我问,因为我找不到任何关于如何使用RxJava实现我上面提到的Node.js异步模式的指南。此外,我正在使用Android,因此没有lambda函数。

3 个答案:

答案 0 :(得分:1)

经过几天的反复试验,我用于此问题的解决方案如下。任何有关改进的建议都是最受欢迎的!

private Observable<File> makeObservable(final String filePath){
    return Observable.create(new ObservableOnSubscribe<File>() {
        @Override
        public void subscribe(final ObservableEmitter<File> e) throws Exception {
            // someAsyncOperation will call the Callback.onResult after
            // having finished the asynchronous operation.
            // Callback<File> is an abstract code I put together for this example
            SomeDatabase.someAsyncOperation(filePath, new Callback<File>(){
                @Override
                public void onResult(Error error, File file){
                    if (error != null){
                        e.onError(error);
                    } else {
                        e.onNext(file);
                        e.onComplete();
                    }
                }
            })
        }
    });
}

private void processFilePaths(ArrayList<String> dataArray){
    ArrayList<Observable<File>> observables = new ArrayList<>();
    for (String filePath : dataArray){
        observables.add(makeObservable(filePath));
    }
    Observable.concat(observables)
            .toList()
            .subscribe(new Consumer<List<File>>() {
                @Override
                public void accept(@NonNull List<File> files) throws Exception {
                    // process results.
                    // Files are now sequentially processed using the asynchronous code.
                }
            });
}

简而言之:

  1. dataArray变为Observable
  2. 数组
  3. 每个Observable在订阅时执行异步操作,并在异步操作完成后将数据提供给onNext
  4. 在Observable数组上使用Observable.concat()以确保顺序执行
  5. 使用.toList()将Observable的结果合并为一个列表
  6. 虽然此代码符合我的要求,但由于以下几个原因,我对此解决方案并不完全满意:

    1. 我不确定在Observable.create()...subscribe(){中执行异步代码是否是使用Observable的正确方法。
    2. 我读到使用Observable.create时应谨慎使用(ref docs
    3. 我不确定我想要实现的行为需要为dataArray中的每个项目创建一个新的Observable。对于我来说,有一个能够按顺序释放数据的可观察对象似乎更自然 - 但同样,这可能只是我原来的思维方式。
    4. 我已经读过,使用concatMap应该可以解决这个问题,但我永远无法弄清楚如何将它应用到这个示例中,并发现concat正在做的伎俩。有人关心解释两者之间的区别吗?
    5. 我之前尝试在Observable数组上使用.zip函数,虽然我最终得到了结果列表,但异步操作并行执行而不是顺序执行。

答案 1 :(得分:0)

执行异步操作(计算等)的一种非常简单易用的方法是使用RxJava Async Utils库(不是我编写的,只是在我们的代码中使用)。

  • 在后台线程中运行方法的最简单方法是使用Async.start(this::methodToCall, Schedulers.io())返回Observable<T>,它在方法调用完成后生成传递方法的返回值。
  • 还有许多其他替代方法可供您使用,例如通过Observer<T>
  • 报告中间结果

如果您想使用Observable.from()concatMap()(或类似方式),请记住首先使用.observerOn(Schedulers.io())或您想要的任何其他计划程序将处理移至后台计划程序。

答案 2 :(得分:0)

要按顺序调用对象数组上的函数,您不需要任何异步工具。只需在数组上进行for循环。 如果您需要该循环以异步方式执行,那么请描述您需要的异步:要么在异步填充数组后启动循环,要么想要异步地对计算结果做出反应,或者两者兼而有之。在任何一种情况下,班级CompletableFuture都可以提供帮助。