如何在vertx中重构异步调用链以避免回调地狱

时间:2018-04-12 15:00:31

标签: java asynchronous callback vert.x

我有以下代码与几个异步调用相互依赖(例如,调用可以是apis REST),并在最后处理所有结果。这是我的示例代码:

private void foo1(String uuid, Handler<AsyncResult<JsonObject>> aHandler) {
    //call json api, for example
    JsonObject foo1 = new JsonObject();
    foo1.put("uuid", "foo1");
    aHandler.handle(Future.succeededFuture(foo1));
 }

private void foo2(String uuid, Handler<AsyncResult<JsonObject>> aHandler) {

    //call json api, for example
    JsonObject foo2 = new JsonObject();
    foo2.put("uuid", "foo2");
    aHandler.handle(Future.succeededFuture(foo2));
 }

private void foo3(String uuid, Handler<AsyncResult<JsonObject>> aHandler) {

    //call json api, for example
    JsonObject foo3 = new JsonObject();
    foo3.put("uuid", "foo3");
    aHandler.handle(Future.succeededFuture(foo3));
 }

private void doSomething(JsonObject result1, JsonObject result2, JsonObject result3, Handler<AsyncResult<JsonObject>> aHandler) {
    JsonObject finalResult =new JsonObject();
    aHandler.handle(Future.succeededFuture(finalResult));
}

private void processToRefactor (String uuid, Handler<AsyncResult<JsonObject>> aHandler) {

    foo1(uuid, ar -> {
        if (ar.succeeded()) {
            JsonObject foo1 = ar.result();
            foo2(foo1.getString("uuid"), ar2 ->{
                if (ar2.succeeded()) {
                    JsonObject foo2 = ar2.result();
                    foo3(foo2.getString("uuid"), ar3 -> {
                        if (ar3.succeeded()) {
                            JsonObject foo3 = ar3.result();
                            doSomething(foo1, foo2, foo3, aHandler);
                        } else {
                            ar3.cause().printStackTrace();
                        }
                    });
                } else {
                    ar2.cause().printStackTrace();
                }
            });
        } else {
            ar.cause().printStackTrace();
        }

    });
}

在前面的代码中,我可以在&#34; doSomething&#34;中使用所有结果。所有呼叫都成功的方法。 我试图使用简单的&#34; HelloWord&#34;重构这个代码。示例来自以下链接https://streamdata.io/blog/vert-x-and-the-async-calls-chain/

这是我的结果:

    private void process(String uuid, Handler<AsyncResult<JsonObject>> aHandler) {

        Future<JsonObject> future = Future.future();
        future.setHandler(aHandler);

        Future<JsonObject> futureFoo1 = Future.future();

        foo1(uuid, futureFoo1);

        futureFoo1.compose(resultFoo1 -> {
            Future<JsonObject> futureFoo2 = Future.future();
            foo2(resultFoo1.getString("uuid"), futureFoo2);

            return futureFoo2; 
        }).compose(resultFoo2 ->{
            Future<JsonObject> futureFoo3 = Future.future();
            foo3(resultFoo2.getString("uuid"), futureFoo3);

            return futureFoo3;

        }).compose(resultFoo3 -> {

            // How to get result1, result2 and result3?
//            doSomething(resultFoo1, resultFoo2, resultFoo3, aHandler);

        }, future);
    }

新代码更清晰,更清晰,但在使用撰写时,在调用函数的时候&#34; doSomething&#34;我没有可用的所有调用结果。 如何在链的末尾获得所有结果?

另一方面,如果其中一个apis调用方法返回一个数组怎么办?也就是说,对于数组的每个元素,应用一系列函数,独立事实上有些人有结果,有些则没有。例如:

private void foo1Array(String uuid, Handler<AsyncResult<JsonArray>> aHandler) {

    //call json api that return array, for example
    JsonArray result = new JsonArray();
    JsonObject foo1 = new JsonObject();
    foo1.put("uuid", "foo1");

    JsonObject foo2 = new JsonObject();
    foo1.put("uuid", "foo2");

    JsonObject foo3 = new JsonObject();
    foo1.put("uuid", "foo3");

    result.add(foo1);
    result.add(foo2);
    result.add(foo3);

    aHandler.handle(Future.succeededFuture(result));
 }   
private void processArray(String uuid, Handler<AsyncResult<JsonObject>> aHandler) {

    Future<JsonObject> future = Future.future();
    future.setHandler(aHandler);

    Future<JsonArray> futureFoo1 = Future.future();

    foo1Array(uuid, futureFoo1);

    futureFoo1.compose(resultArray -> {
        List<Future> futures = new ArrayList<Future>();
        for (int i = 0; i < resultArray.size(); i ++) {
            JsonObject resultFoo1 = resultArray.getJsonObject(i);

            Future<JsonObject> futureFoo2 = Future.future();
            foo2(resultFoo1.getString("uuid"), aHandler);
            futures.add(futureFoo2);
        }

        CompositeFuture.any(futures).setHandler(ar -> {
            //What to do here?
        });

    }, future);
}

如何使用foo1Array的结果调用函数foo2,foo3,...然后在doSomething中使用它?

2 个答案:

答案 0 :(得分:1)

你的初步方法实际上并不太糟糕。

为了改善代码以实现更好的&#34;可组合性&#34;,您应该将每个fooX方法的处理程序输入arg更改为扩展Handler<AsyncResult<JsonObject>>(例如Future)的内容,并返回相同的处理程序作为结果,因此它在`Future.compose中变得更好用,因为传入的处理程序可以用作每个组合的返回值:

 private <T extends Handler<AsyncResult<JsonObject>>> T foo1(String uuid, T aHandler) {
    JsonObject foo1 = new JsonObject().put("uuid", "foo1");
    aHandler.handle(Future.succeededFuture(foo1));
    return aHandler; //<-- return the handler here
}

其次,为了在最后阶段访问所有三个结果,您必须在链外宣布三个期货。现在,你可以使用每个foo方法的输出很好地链接期货,作为每个组合的结果。

Future<JsonObject> futureFoo1 = Future.future();
Future<JsonObject> futureFoo2 = Future.future();
Future<JsonObject> futureFoo3 = Future.future();


foo1(uuid, futureFoo1).compose(resultFoo1 -> foo2(resultFoo1.getString("uuid"), futureFoo2))
                      .compose(resultFoo2 -> foo3(resultFoo2.getString("uuid"), futureFoo3))
                      .compose(resultFoo3 -> doSomething(futureFoo1.result(), //access results from 1st call
                                                         futureFoo2.result(), //access results from 2nd call 
                                                         resultFoo3,
                                                         Future.<JsonObject>future().setHandler(aHandler))); //pass the final result to the original handler

如果你不能忍受&#34;杂质&#34;这种方法(定义外链的期货并在函数内修改它们),你必须传递每个方法的原始输入值(=前一次调用的输出)以及结果,但我怀疑这会使代码更多可读的。

为了在一个compose方法中改变类型,你必须使用fooX方法 转换,不返回原始处理程序,而是返回具有不同类型的新Future

private Future<JsonArray> foo2(String uuid, Handler<AsyncResult<JsonObject>> aHandler) {
    JsonObject foo2 = new JsonObject();
    foo2.put("uuid", "foo2" + uuid);
    aHandler.handle(Future.succeededFuture(foo2));
    JsonArray arr = new JsonArray().add("123").add("456").add("789");
    return Future.succeededFuture(arr);
}

答案 1 :(得分:0)

如果您想要更简单且没有回调地狱代码然后切换到rx,您的上述代码将变成这样的内容:

private Single<JsonObject> foo1(String uuid) {
    JsonObject foo1 = new JsonObject();
    foo1.put("uuid", "foo1");
    return Single.just(foo1);
 }

private Single<JsonObject> foo2(String uuid) {
    JsonObject foo2 = new JsonObject();
    foo2.put("uuid", "foo2");
    return Single.just(foo2);
 }

private Single<JsonObject> foo3(String uuid) {
    JsonObject foo3 = new JsonObject();
    foo3.put("uuid", "foo3");
    return Single.just(foo3);
 }

private Single<JsonObject> doSomething(String uuid) {
    JsonObject finalResult =new JsonObject();
    return Single.zip(foo1(uuid), foo2(uuid), foo3(uuid))
    .map(results -> {
        // Map zip results to finalResult
        return finalResult;
    });
}

private void processToRefactor (String uuid, Handler<AsyncResult<JsonObject>> aHandler) {
    doSomething(uuid).subscribe();
}