在异步执行时停止功能链执行的最佳实践是什么?

时间:2019-09-25 13:34:38

标签: java java-8

我在这里有此功能:

Function<Integer, Integer> func = (value) -> value + 5;
        func = func.andThen((value) -> {
            //Imagine that here some code executed and raised an exception, I'm throwing it 
            //manually just for the sake of this example.
            throw new RuntimeException("failed");
        });
        func = func.andThen((value) -> {
            System.out.println("Reached last function !");
            return value;
        });
        executeFunction(func);

现在,您可以看到我在第一个andThen方法中抛出了运行时异常。那是因为我想防止第二个andThen被执行。那是最好的方法吗?

此外,我注意到,如果我在另一个线程(异步)中执行此功能,则异常不会打印在控制台中,并且我想知道该异常发生了。

private static void executeFunction(Function<Integer, Integer> function) {
        CompletableFuture.supplyAsync(() -> function.apply(100));
    }

在这种情况下,如果我想确保记录了异常,但是并没有执行andThen链中的下一个函数,我应该记录并抛出吗?这不是ati模式吗?

3 个答案:

答案 0 :(得分:1)

实例化和引发大量异常可能变得非常昂贵,这就是为什么应该将它们限制在特殊情况下的原因。相反,您可以使用Optional进行控制:

>>> combine_lists([1, 2, 3], [4])
[1, 4, 2, 3]
>>> combine_lists([1], [4, 5, 6])
[1, 4, 5, 6]
>>> combine_lists([], [4, 5, 6])
[4, 5, 6]
>>> combine_lists([1, 2], [4, 5, 6])
[1, 4, 2, 5, 6]

答案 1 :(得分:0)

您可以编写函数/可运行程序的包装,在任务失败时记录并退出。像这样:

class Runnables
{
    public static Runnable trying(Runnable... runnables)
    {
        return () ->
        {
            int successes = 0;
            try
            {
                for(Runnable runnable : runnables)
                {
                    runnable.run();
                    successes++;
                }
            }
            catch(Throwable t)
            {
                logger.error("Exception thrown from "+successes+"th runnable: ",t);
            }
        };
    }
}

然后:

private static void executeFunction(Runnable... runnables)
{
    CompletableFuture.supplyAsync(Runnables.trying(runnables));
}

答案 2 :(得分:0)

您可以使用CompletableFuture.thenApply方法来获得所需的行为。例如:

public class Answer {
  public static void main(String[] args) {
    Function<Integer, Integer> fn0 = v -> v + 5;

    Function<Integer, Integer> fn1 = v -> {
        throw new RuntimeException("failed");
    };

    Function<Integer, Integer> fn2 = v -> {
        System.out.println("Reached last function !");
        return v;
    };

    CompletableFuture.supplyAsync(() -> fn0.apply(100))
            .thenApply(fn1)
            .thenApply(fn2)
            .exceptionally(throwable -> {
                // next line prints the exception thrown by fn1, wrapped in java.util.concurrent.CompletionException
                System.out.println("Failed with error: " + throwable); 
                return 0; // default value, used when exception is thrown
            });
  }
}

CompletableFuture链基本上会被“开箱即用”异常中断,因此不需要其他处理。

或者,如果您想使用更通用的方法:

public class Answer {
  public static void main(String[] args) {
    executeAsync(() -> stepOne(100))
            .thenApply(Answer::stepTwo)
            .thenApply(Answer::finalStep)
            .exceptionally(Answer::handleException);
  }

  private static CompletableFuture<Integer> executeAsync(Supplier<Integer> task) {
    return CompletableFuture.supplyAsync(task::get);
  }

  private static Integer stepOne(Integer value) {
    return value + 5;
  }

  private static Integer stepTwo(Integer value) {
    throw new RuntimeException("failed");
  }

  private static Integer finalStep(Integer value) {
    System.out.println("Reached last function !");
    return value;
  }

  private static Integer handleException(Throwable throwable) {
    // next line prints the exception thrown by any step before, wrapped in java.util.concurrent.CompletionException
    System.out.println("Failed with error: " + throwable);
    return 0; // default value
  }

注意:

  • 使用thenApply可以根据需要链接任意数量的函数调用

  • 在最后一个示例中,同一类内的方法可以被其他类(不一定是静态类)中的方法替换