18
CompletableFuture.supplyAsync(
() -> {
    transporter.write(req);
    //here take the value from a blocking queue,will throw a interruptedException
    return responseQueue.take();
},  executorService);

The common method to deal with interruptedException is either to interrupt again or direct throw interruptedException, but both cannot work. Anyone have the idea?

assylias
  • 321,522
  • 82
  • 660
  • 783
GrapeBaBa
  • 1,391
  • 3
  • 12
  • 22
  • "*but both cannot work.*" => why? – assylias Apr 20 '14 at 16:59
  • both have compiler error. if direct throw exception, compiler will show unhandled exception, if catch it and call Thead.current.interrupt, compiler will show must return a T type. – GrapeBaBa Apr 20 '14 at 17:04
  • Yes you need to return or throw. If you decide to return null, for example: `try { return queue.take(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; }` – assylias Apr 20 '14 at 17:19
  • suppose future represent a compute result which is a normal value or a exception, i feel it should be a way set the exception to the future, it is better than set null. – GrapeBaBa Apr 20 '14 at 17:28
  • I think lambda functions don't support throwing exceptions so throwing an exception is out of question. The sole thing you can do here is to return something. – mostruash Apr 20 '14 at 17:29
  • @GrapeBaBa you can also throw a `RuntimeException` and capture it with `CompletableFuture.runAsync(...).exceptionally(t -> /*deal with it here*/);` – assylias Apr 20 '14 at 17:30
  • @assylias runAsync return future, seems no return type – GrapeBaBa Apr 21 '14 at 01:11
  • *When are you going to be interrupting your `ForkJoinPool.commonPool()` and what does it mean for you to do this?* The answer to this question is prerequisite to how you handle your interrupts. If you couldn't care less, rethrow it as an `AssertionError` and forget about it. If you want to handle it for the general case, rethrow it as `java.util.concurrent.CompletionException`, as is normal for any checked exceptions that occur during `CompletedFuture` stages. – antak Aug 20 '15 at 01:52
  • Here is a solution that allows you to use checked exceptions without reducing the readability of your code: https://stackoverflow.com/a/49705336/14731 – Gili Apr 07 '18 at 08:23

4 Answers4

19

I change the code like this.

    CompletableFuture<Rep> result = new CompletableFuture<>();
    CompletableFuture.runAsync(() -> {

        transporter.write(req);
        try {
            Rep rep = responseQueue.take();
            result.complete(rep);
        } catch (InterruptedException e) {
            result.completeExceptionally(e);
            Thread.currentThread().interrupt();
        } catch (Exception e) {
            result.completeExceptionally(e);
        }

    }, executorService);
    return result;
GrapeBaBa
  • 1,391
  • 3
  • 12
  • 22
  • What you have done is equivalent to mine. Alas, `CompletableFuture result` can be replaced by ANY class that conforms to the "result or exception" paradigm. For example you can add `get`, `complete` and `completeExceptionally` methods to `ResultWrapper` and use `ResultWrapper rep = new ResultWrapper();`. It's quite a coincidence that you encountered this lambda function limitation using `CompletableFuture` and again you solved it using `CompletableFuture`, making use of those methods that you used. – mostruash Apr 22 '14 at 20:26
  • 2
    Yes,but completablefuture already has the abstraction for a compute result,so I don't want to create new type for the same. – GrapeBaBa Apr 23 '14 at 13:21
  • If you are working with other people, this may confuse the readers of your code. If not, great that it worked for you. – mostruash Apr 23 '14 at 15:07
  • That is because future and promise two concepts into one type completablefuture in java. Return an completablefuture could make all the invoke asynchronous due to there are many methods in it. – GrapeBaBa Apr 23 '14 at 15:43
  • CompletableFuture.runAsync use is not really necessary here, since you are not using return value; if you want to save some cycles, you can create ForJoinTask and execute it in ForkJoinPool#commonPool. Its slightly more code, but you'll avoid creating few instances and writing to volatile fields, so it should perform better. – Pavel Bucek Jan 09 '15 at 09:41
  • -1: This redundant use of `CompletableFuture` isn't advice anyone should be following. Unfortunately, this page ranks highly in Google search for *CompletableFuture checked exception*. – antak Aug 20 '15 at 01:55
  • While this solution is much more straightforward and elegant (in my view at least) than the one with the wrapper class, the use of `runAsync` is a bit of a "code smell". Given an executor service, you should just submit the runnable to it, without creating an additional `CompletableFuture`. – Itai Jan 29 '18 at 13:15
  • Instead of using an extra `CompletableFuture`, you should just `throw CompletionException(e)`. Note the following from the javadoc: *In case of exceptional completion with a CompletionException, methods get() and get(long, TimeUnit) throw an ExecutionException with the same cause as held in the corresponding CompletionException. To simplify usage in most contexts, this class also defines methods join() and getNow(T) that instead throw the CompletionException directly in these cases.* – cambunctious Jun 11 '19 at 17:20
5

I ran into the same question, but after reading more from comments here and reference book I think you can do either one of these two:

1 (what I end up doing):

CompletableFuture.runAsync(() -> {
    transporter.write(req);
    try {
        Rep rep = responseQueue.take();
        result.complete(rep);
    } catch (Exception e) {
        throw new CompletionException(e);
    }
}, executorService);
return result;

or 2:

CompletableFuture<Rep> result = new CompletableFuture<>();
new Thread(()-> {
    transporter.write(req);
    try {
        Rep rep = responseQueue.take();
        result.complete(rep);
    } catch (Exception e) {
        result.completeExceptionally(e);
    }
}).start();

I know the 2nd one does not use the executorService, but I feel the whole point of using CompletableFuture is utilizing the CompletionStage APIs in functional-style.

ThinkBonobo
  • 15,487
  • 9
  • 65
  • 80
derrdji
  • 12,661
  • 21
  • 68
  • 78
  • 1
    Your second solutions can be improved. See http://stackoverflow.com/a/28961083/868941 for detailled explanation. – rmuller Jul 21 '16 at 06:16
  • the second solution is quite inefficient, i hope u dont reall start a new thread every time you want to run some async code – leozilla Jan 06 '21 at 12:21
3

@antak mentioned it buried in a comment, but I think the correct answer here is:

For CompletableFuture.supplyAsync() wrap it in java.util.concurrent.CompletionException and rethrow it.

So the sample code would look something like:

CompletableFuture.supplyAsync(
    () -> {
        transporter.write(req);
        try {
            //here take the value from a blocking queue,will throw a interruptedException
            return responseQueue.take();
        }
        catch (InterruptedException e) {
            throw new CompletionException(e);
        }
    },  executorService);
xbakesx
  • 13,202
  • 6
  • 48
  • 76
  • is there any alternative to avoid try-catch here? – Gaurav Oct 15 '19 at 09:06
  • 1
    I don't think so. You could create your own implementation of a BlockingQueue that throws `CompletionException` instead of `InterruptedException`... But that doesn't seem like it's really helping you. – xbakesx Oct 15 '19 at 14:10
2

As lambda functions don't support throwing exceptions, I think Java developers will need a new paradigm. One thing that comes to mind is as follows:

public class ResultWrapper<R, E extends Exception> {
    E exception;
    R result;
}

Lambda functions can return instances of this wrapper. (Edit: your case)

CompletableFuture<ResultWrapper<String, InterruptedException>> aFuture = ...;
...
aFuture.supplyAsync(
() -> {
    try {
        transporter.write(req);
    } catch(InterruptedException e) {
        ResultWrapper<String, InterruptedException> r = new ResultWrapper<>();
        r.exception = e;
        r.result = null;
        return r;
    }
    ...
},  executorService);
mostruash
  • 4,169
  • 1
  • 23
  • 40
  • Please mark it as answer if you think it helped you :) – mostruash Apr 21 '14 at 01:13
  • Instead of converting exceptions to a return value, you should wrap them in an unchecked exception and translate them back to a checked exception on the outside if necessary. Keep exceptions as exceptions the whole way through. Return values are reserved for non-error conditions. – Gili Oct 26 '14 at 08:30
  • Although I agree to some extent, consider a producer/consumer scenario where produced/consumed objects are lambda functions. Consumers must catch unchecked exceptions because a producer might have thrown an unchecked exception to smuggle out a checked exception. Not sure which is worse, it's the Java that sucks I guess. – mostruash Oct 26 '14 at 09:05
  • 3
    Don't reinvent the wheel.. For `CompletableFuture.supplyAsync()` wrap it in `java.util.concurrent.CompletionException` and rethrow it. – antak Aug 20 '15 at 01:44