简体   繁体   中英

Unable to propagate custom exception from exceptionally in CompletableFuture

           public void executeAsync(Task task){
             CompletableFuture.runAsync(
                () -> {
                    task.execute(CompletableFuture.completedFuture(null), executor)
                            .exceptionally(
                                    ex -> {
                                        log.error(ex.getMessage(), ex);
                                        log.debug(
                                                "exception from executor" + ex.getStackTrace());
                                        throw new RunTimeException(
                                                ex);
                                    })
                            .join();
                },
                executor);
     }


    public static Task execute(Task... tasks) {
    return (future, executor) ->
            tasks.length == 0
                    ? future
                    : CompletableFuture.allOf(
                            Arrays.stream(tasks)
                                    .map(t -> t.execute(future, executor))
                                    .toArray(CompletableFuture[]::new));
}

Here in the above code, I can see exceptionlogging from executeAsync() method for one of the tasks, however for calling function for this method is unable to catch Exception thrown from it.

Any pointers will be helpful in this regard, seems nothing is working for me. I need to catch any exception thrown from any of the tasks and update the DB accordingly.

As far as I know, the only asynchronous library which is able to handle errors in layers is DF4J (developed by me). A layer is defined as Dataflow object:

 Dataflow upper = new Dataflow();  
 Dataflow nested = new Dataflow(upper);

Dataflow is a graph consisting of asynchronous procedures and nested Dataflow s. When an exception is thrown from an async procedure, it is propagated to parent dataflows. We can monitor only upper-level dataflow for all errors.

The class most close to CompletableFuture is AsyncFunc . AsyncFunc s may have asynchronous parameters to get values from other AsyncFunc s or CompletableFuture s, but in this example we do not use them.

For each kind of AsyncFunc it is recommended to declare its own class:

class StringToInt extends AsyncFunc<Integer> {
    String argumnet;

    // in constructor, link this async function to the parent dataflow
    public StringToInt(Dataflow df, String argumnet) {
        super(df);
        this.argumnet = argumnet;
    }

    @Override
    protected Integer callAction() throws Throwable {
        return Integer.valueOf(argumnet); // can throw NumberFormatException
    }
}

This class can be used as follows:

    // start good asyncFunc tied to upper dataflow
    new StringToInt(upper, "10").start();
    // start bad asyncFunc tied to nested dataflow
    new StringToInt(nested, "not an integer").start();
    try {
        // here wait all started async functions to finish
        upper.blockingAwait(100);
        Assert.fail("exception expected");
    } catch (CompletionException e) {
       // here we catch first error from all async functions
        Throwable cause = e.getCause();
        cause.printStackTrace();
        Assert.assertEquals(NumberFormatException.class, cause.getClass());
    }

It is recommended to use the latest version of DF4J cloned from Github.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM