简体   繁体   中英

Are `thenRunAsync(…)` and `CompletableFuture.runAsync(() -> { … });` related at all?

I need to perform some extra tasks but let the original thread finish up, eg send back an HTTP response.

I think I can just do this:

return mainTasksFuture.thenApply(response -> {
  CompletableFuture.runAsync(() -> {
    // extra tasks
  });
  return response;
});

But I remembered there's a thenRunAsync . Is

return mainTasksFuture.thenApply(response -> {
  return response;
}).thenRunAsync(() -> {
  // extra tasks
});

basically another way to do the same thing? In other words, are the then*Async methods terminators (completion methods) that return the previous chain's result in the original thread, then spawn a new thread to execute the rest?

I'm almost certain the answer is no . It just seems it might be that purely based on method names, to someone new to CompletableFutures. I wanted a confirmation though, in case what I'm reading about ForkJoinPool.commonPool is actually saying what I'm doubting, just in a different way.

Both runAsync and thenRunAsync execute the Runnable taks asynchronous

executes the given action using this stage's default asynchronous execution facility

Question : In other words, are the then*Async methods terminators (completion methods) that return the previous chain's result in the original thread, then spawn a new thread to execute the rest?

Answer: No, From documentation One stage's execution may be triggered by completion of a single stage, or both of two stages, or either of two stages .So basically the result might be returned based on how programmer coded that part, but now in your case (using thenRunAsync ) the result will be returned after first stage completion because in the second stage thenRunAsync you are taking result from first stage as input but not returning anything.

Interface CompletionStage

One stage's execution may be triggered by completion of a single stage, or both of two stages, or either of two stages. Dependencies on a single stage are arranged using methods with prefix then. Those triggered by completion of both of two stages may combine their results or effects, using correspondingly named methods. Those triggered by either of two stages make no guarantees about which of the results or effects are used for the dependent stage's computation.

There is also a slight difference between first example and second example

Example : 1 In this example the Runnable tasks get executed asynchronously before returning the result, both Function from thenApply and Runnable from runAsync will be executed concurrently

return mainTasksFuture.thenApply(response -> {
 CompletableFuture.runAsync(() -> {
   // extra tasks
   });
  return response;
 });

Example : 2 In this example Runnable task from thenRunAsync will be executed after completion of Function from thenApply

return mainTasksFuture.thenApply(response -> {
return response;
}).thenRunAsync(() -> {
  // extra tasks
});

You wrote

It just ∗seems* it might be that purely based on method names, to someone new to CompletableFutures.

Well, the method names correctly reflect what the methods do. Both, runAsync and thenRunAsync initiate the asynchronous execution of a Runnable and return a future, which will be completed when the asynchronous execution has finished. So the similarity in the names is justified.

It's your code which is fundamentally different.

In this variant

return mainTasksFuture.thenApply(response -> {
  CompletableFuture.runAsync(() -> {
    // extra tasks
  });
  return response;
});

you are ignoring the future returned by runAsync entirely, so the future returned by thenApply will be completed as soon as the asynchronous operation has been triggered. The caller can retrieve the result value while the “extra tasks” are still running concurrently.

In contrast, with

    return mainTasksFuture.thenApply(response -> {
        return response;
    }).thenRunAsync(() -> {
      // extra tasks
    });

the thenApply is entirely obsolete as it doesn't do anything. But you are returning the future returned by thenRunAsync , which will be completed when the asynchronous execution of the Runnable has finished and has the type CompletableFuture<Void> , as the runnable does not produce a value (the future will be completed with null ). In the exceptional case, it would get completed with the exception of mainTasksFuture , but in the successful case, it does not pass through the result value.

If the first variant matches your actual intention (the caller should not depend on the completion of the extra tasks), simply don't model them as a dependency:

mainTasksFuture.thenRunAsync(() -> {
    // extra tasks
});
return mainTasksFuture; // does not depend on the completion of extra task

Otherwise, stay with variant 2 (minus obsolete things)

return mainTasksFuture.thenRunAsync(() -> {
  // extra tasks
}); // depends on the completion of extra task but results in (Void)null

if you don't need the result value. Otherwise, you can use

return mainTasksFuture.thenApplyAsync(response -> {
    // extra tasks
    return response;
}); // depends on the completion of extra task and returns original result

it would be the same as with

return mainTasksFuture.thenCompose(response ->
    CompletableFuture.runAsync(() -> {
        // extra tasks
    }).thenApply(_void -> response));

which does not ignore the future returned by runAsync , but there's no advantage in this complication, compared to thenApplyAsync .

Another alternative would be

return mainTasksFuture.whenComplete((response,failure) -> {
    if(failure == null) {
        // extra tasks
    }
});

as the future returned by whenComplete will get completed with the original future's result when the extra tasks have been completed. But the function is always evaluated, even when the original future completed exceptionally, so it needs another conditional if that's not desired.

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