When to use non-async methods of CompletableFuture

2020-08-25 05:08发布

问题:

I (mostly) understand the three execution methods of CompletableFuture:

  • non-async (synchronous execution)
  • default async (asynchronous using the default executor)
  • custom async (asynchronous using a custom executor)

My question is: when should one favor the use of non-async methods?

What happens if you have a code block that invokes other methods that also return CompletableFutures? This might look cheap on the surface, but what happens if those methods also use non-async invocation? Doesn't this add up to one long non-async block that could get expensive?

Should one restrict the use of non-async execution to short, well-defined code-blocks that do not invoke other methods?

回答1:

When should one favor the use of non-async methods?

The decision for continuations is no different than for the antecedent task itself. When do you choose to make an operation asynchronous (e.g., using a CompletableFuture) vs. writing purely synchronous code? The same guidance applies here.

If you are simply consuming the result or using the completion signal to kick off another asynchronous operation, then that itself is a cheap operation, and there is no reason not to use the synchronous completion methods.

On the other hand, if you are chaining together multiple long-running operations that would each be an async operation in their own right, then use the async completion methods.

If you're somewhere in between, trust your gut, or just go with the async completion methods. If you're not coordinating thousands of tasks, then you won't be adding a whole lot of overhead.

Should one restrict the use of non-async execution to short, well-defined code-blocks that do not invoke other methods?

I would use them for operations that are not long-running. You don't need to restrict their use to trivially short and simple callbacks. But I think you have the right idea.

If you're using CompletableFuture, then you have decided that at least some operations in your code base necessitate async execution, but presumably not all operations are async. How did you decide which should be async and which should not? If you apply that same analysis to continuations, I think you'll be fine.

What happens if you have a code block that invokes other methods that also return CompletableFutures? This might look cheap on the surface, but what happens if those methods also use non-async invocation? Doesn't this add up to one long non-async block that could get expensive?

Returning a CompletableFuture generally signifies that the underlying operation is scheduled to occur asynchronously, so that should not be a problem. In most cases, I would expect the flow to look something like this:

  1. You synchronously call an async method returning a CompletableFuture. It schedules some async operation to eventually provide a result. Your call returns almost immediately, with no blocking.
  2. Upon completion, one or more continuations may be invoked. Some of those may invoke additional async operations. Those will call into methods that will schedule additional async operations, but as before, they return almost immediately.
  3. Go to (2), or finish.