# Interface StructuredTaskScope.Joiner<T, R, R_X extends Throwable>

Type Parameters:
  `T` - the result type of subtasks forkedPREVIEW in the scope
  `R` - the type of the result returned by the `join()`PREVIEW method
  `R_X` - the type of the exception thrown by the `join()`PREVIEW method

Enclosing interface:
  `StructuredTaskScopePREVIEW<T, R, R_X extends Throwable>`

public static interface StructuredTaskScope.Joiner<T, R, R_X extends Throwable>
`Joiner` is a preview API of the Java platform.
Programs can only use `Joiner` when preview features are enabled.
Preview features may be removed in a future release, or upgraded to permanent features of the Java platform.
An object used with a `StructuredTaskScope`PREVIEW to produce the outcome for the scope's `join()`PREVIEW method.

A `StructuredTaskScope` is opened with a `Joiner` that handles subtasks as they are forkedPREVIEW in the scope and again when they complete execution. The `Joiner` handles subtasks that complete successfully with a result of type `T`, or fail with any exception or error. The `Joiner` implements a policy that may cancel the scope when some condition is reached (for example, a subtask fails). When all subtasks complete execution, or the scope is cancelled, the `Joiner` produces the outcome for the `join()`PREVIEW method. The outcome is a result of type `R` or an exception of type `R_X`.

`Joiner` defines static methods to create `Joiner` objects for common cases:

- `allSuccessfulOrThrow()` and `allSuccessfulOrThrow(Function)` create a `Joiner` that produces a list of all results for `join()` to return when all subtasks complete successfully. The `Joiner` cancels the scope and causes `join()` to throw `ExecutionException`, or an exception returned by an exception supplying function, if any subtask fails.
- `anySuccessfulOrThrow()` and `anySuccessfulOrThrow(Function)` create a `Joiner` that produces the result of any successful subtask for `join()` to return. The `Joiner` causes `join()` to throw `ExecutionException`, or an exception returned by an exception supplying function, if all subtasks fail.
- `awaitAllSuccessfulOrThrow()` and `awaitAllSuccessfulOrThrow(Function)` create a `Joiner` that waits for all subtasks to complete successfully. The `Joiner` does not produce a non-`null` result for `join()` to return. The `Joiner` cancels the scope and causes `join()` to throw `ExecutionException`, or an exception returned by an exception supplying function, if any subtask fails.

In addition to the methods to create `Joiner` objects for common cases, the `allUntil(Predicate)` method can be used to create a `Joiner` that implements a cancellation policy. The `Joiner` is created with a predicate that is evaluated on completed subtasks. The `Joiner` cancels the scope if the predicate evaluates to `true`. When using this `Joiner`, the outcome of the `join()` method is the list of all subtasks forked in the scope.

More advanced policies can be developed by implementing the `Joiner` interface. The `onFork(Subtask)` method is invoked when subtasks are forked. The `onComplete(Subtask)` method is invoked when subtasks complete with a result or exception. These methods return a `boolean` to indicate whether the scope should be cancelled. These methods can be used to collect subtasks, results, or exceptions, and control when to cancel the scope. The `result()` method must be implemented to produce the outcome (result or exception) for the `join()` method. The `timeout()` method must be implemented to produce the outcome for the timeoutPREVIEW case.

Unless otherwise specified, passing a `null` argument to a method in this class will cause a `NullPointerException` to be thrown.

API Note:
  It is very important that a new `Joiner` object is created for each `StructuredTaskScope`. `Joiner` objects should never be shared with different scopes or re-used after a scope is closed.
Implementation Requirements:
  Implementations of this interface must be thread-safe. The `onComplete(Subtask)` method may be invoked concurrently as multiple subtasks can complete at the same time. Additionally, the `onComplete(Subtask)` method may be called concurrently with the scope owner thread invoking the `onFork(Subtask)`, `result()`, or `timeout()` methods.

  A `Joiner` should clearly document how it handles timeoutsPREVIEW. In many cases, a timeout will cause the `join()` method to throw an exception with `CancelledByTimeoutException`PREVIEW as the cause. Some `Joiner` implementation may be capable of returning a result for the timeout case.

  Designing a `Joiner` should take into account the code at the use-site where the results from the `join()`PREVIEW method are processed. It should be clear what the `Joiner` does vs. the application code at the use-site. In general, the `Joiner` implementation is not the place for "business logic". A `Joiner` should be designed to be as general purpose as possible.

Since:
  25
See Also:
  - `StructuredTaskScope.open(Joiner)`PREVIEW

## Method Summary

Modifier and Type
Method
Description
`static <T> StructuredTaskScope.JoinerPREVIEW<T, List<T>, ExecutionException>`
`allSuccessfulOrThrow()`
Returns a new Joiner that produces a list of all results when all subtasks complete successfully.
`static <T, R_X extends Throwable>
StructuredTaskScope.JoinerPREVIEW<T,List<T>,R_X>`
`allSuccessfulOrThrow(Function<Throwable, R_X> esf)`
Returns a new Joiner that produces a list of all results when all subtasks complete successfully.
`static <T> StructuredTaskScope.JoinerPREVIEW<T, List<StructuredTaskScope.SubtaskPREVIEW<T>>, RuntimeException>`
`allUntil(Predicate<? super StructuredTaskScope.SubtaskPREVIEW<T>> isDone)`
Returns a new Joiner that produces a list of all subtasks when all subtasks complete or evaluating a predicate on a completed subtask causes the scope to be cancelled.
`static <T> StructuredTaskScope.JoinerPREVIEW<T, T, ExecutionException>`
`anySuccessfulOrThrow()`
Returns a new Joiner that produces the result of any successful subtask.
`static <T, R_X extends Throwable>
StructuredTaskScope.JoinerPREVIEW<T,T,R_X>`
`anySuccessfulOrThrow(Function<Throwable, R_X> esf)`
Returns a new Joiner that produces the result of any successful subtask.
`static <T> StructuredTaskScope.JoinerPREVIEW<T, Void, ExecutionException>`
`awaitAllSuccessfulOrThrow()`
Returns a new Joiner that causes the `join()`PREVIEW method to wait for all subtasks to complete successfully.
`static <T, R_X extends Throwable>
StructuredTaskScope.JoinerPREVIEW<T,Void,R_X>`
`awaitAllSuccessfulOrThrow(Function<Throwable, R_X> esf)`
Returns a new Joiner that causes the `join()`PREVIEW method to wait for all subtasks to complete successfully.
`default boolean`
`onComplete(StructuredTaskScope.SubtaskPREVIEW<T> subtask)`
Invoked by the thread that executed a subtask after the subtask completes successfully or fails with an exception.
`default boolean`
`onFork(StructuredTaskScope.SubtaskPREVIEW<T> subtask)`
Invoked by `fork(Callable)`PREVIEW and `fork(Runnable)`PREVIEW when forking a subtask.
`R`
`result()`
Invoked by the `join()`PREVIEW method to produce the outcome (result or exception) after waiting for all subtasks to complete or the scope is cancelled.
`R`
`timeout()`
Invoked by the `join()`PREVIEW method to produce the outcome (result or exception) when the scope was opened with a timeout and the timeout expires before or while waiting in the `join()` method.

## Method Details

### onFork

default boolean onFork(StructuredTaskScope.SubtaskPREVIEW<T> subtask)
Invoked by `fork(Callable)`PREVIEW and `fork(Runnable)`PREVIEW when forking a subtask. The method is invoked before a thread is created to execute the subtask.

API Note:
  This method is invoked by the `fork` methods. It should not be invoked directly.
Implementation Requirements:
  The default implementation throws `NullPointerException` if the subtask is `null`. It throws `IllegalArgumentException` if the subtask is not in the `UNAVAILABLE`PREVIEW state, it otherwise returns `false`.
Parameters:
  `subtask` - the subtask
Returns:
  `true` to cancel the scope, otherwise `false`

### onComplete

default boolean onComplete(StructuredTaskScope.SubtaskPREVIEW<T> subtask)
Invoked by the thread that executed a subtask after the subtask completes successfully or fails with an exception. This method is not invoked by subtasks that complete after the scope is cancelled.

API Note:
  This method is invoked by subtasks when they complete. It should not be invoked directly.
Implementation Requirements:
  The default implementation throws `NullPointerException` if the subtask is `null`. It throws `IllegalArgumentException` if the subtask is not in the `SUCCESS`PREVIEW or `FAILED`PREVIEW state, it otherwise returns `false`.
Parameters:
  `subtask` - the subtask
Returns:
  `true` to cancel the scope, otherwise `false`

### result

R result() throws R_X
Invoked by the `join()`PREVIEW method to produce the outcome (result or exception) after waiting for all subtasks to complete or the scope is cancelled. This method is not invoked if the scope was opened with a timeout and the timeout expires before or while waiting.

This method will be called at most once, by the `join()` method, to produce the outcome. The behavior of this method when invoked directly is undefined.

API Note:
  This method is invoked by the `join()` method. It should not be invoked directly.
Returns:
  the result
Throws:
  `R_X` - if the outcome is an exception

### timeout

R timeout() throws R_X
Invoked by the `join()`PREVIEW method to produce the outcome (result or exception) when the scope was opened with a timeout and the timeout expires before or while waiting in the `join()` method.

If the outcome is an exception, this method throws the exception with a `CancelledByTimeoutException`PREVIEW as the `cause`.

This method will be called at most once, by the `join()` method, to produce the outcome. The behavior of this method when invoked directly is undefined.

API Note:
  This method is invoked by the `join()` method. It should not be invoked directly.
Returns:
  the result
Throws:
  `R_X` - with a cause of `CancelledByTimeoutException`, if the outcome is an exception
Since:
  27

### allSuccessfulOrThrow

static <T, R_X extends Throwable> StructuredTaskScope.JoinerPREVIEW<T,List<T>,R_X> allSuccessfulOrThrow(Function<Throwable, R_X> esf)
Returns a new Joiner that produces a list of all results when all subtasks complete successfully. The `Joiner` cancels the scope and causes the `join()`PREVIEW method to throw if any subtask fails.

The `join()`PREVIEW method of a `StructuredTaskScope`PREVIEW opened with this Joiner returns the list of the results, in the order that the subtasks were forkedPREVIEW, when all subtasks complete successfully. An empty list is returned if no subtasks were forked. If any subtask fails then the `Joiner` causes the `join()` method to throw the exception returned by the given exception supplying function when applied to the exception from the first subtask to fail. The function should return an exception with the exception from the failed subtask (the function argument) as the cause. If the function returns `null` then it causes the `join()` method to throw `NullPointerException`.

Timeout Handling: The `Joiner` cannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeoutPREVIEW, and the timeout expires before or while waiting for all subtasks to complete successfully, then the `Joiner` causes the `join()` method to throw the exception returned by the exception supplying function when applied to a `CancelledByTimeoutException`PREVIEW.

API Note:
  Joiners returned by this method are suited to cases where all subtasks return a result of the same type. It removes the need for bookkeeping and the need to keep a reference to the `Subtask`PREVIEW objects returned by the `fork(Callable)`PREVIEW method. Joiners returned by `awaitAllSuccessfulOrThrow(Function)` and `awaitAllSuccessfulOrThrow()` are suited to cases where the subtasks return results of different types and where it is necessary to keep a reference to the `Subtask` objects.

  The following example is a method that opens a `StructuredTaskScope` with a Joiner created with `allSuccessfulOrThrow(Function)`. The method is invoked with a collection of callables. It forksPREVIEW a subtask to execute each callable, waits in `join()`PREVIEW for all subtasks to complete successfully, and then returns a list of the results. It throws the runtime exception `CompletionException` if any subtask fails, with the exception from the first subtask to fail as the cause.

        <T> List<T> invokeAll(Collection<Callable<T>> tasks) throws InterruptedException {
              try (var scope = StructuredTaskScope.open(
                      Joiner.<T, CompletionException>allSuccessfulOrThrow(CompletionException::new))) {
                  tasks.forEach(scope::fork);
                  List<T> results = scope.join();
                  return results;
              }
        }

Type Parameters:
  `T` - the result type of subtasks
  `R_X` - the type of the exception thrown by the `join()`PREVIEW method
Parameters:
  `esf` - the exception supplying function
Returns:
  a new Joiner that produces a list of all results when all subtasks complete successfully
Since:
  27
See Also:
  - `allSuccessfulOrThrow()`

### allSuccessfulOrThrow

static <T> StructuredTaskScope.JoinerPREVIEW<T, List<T>, ExecutionException> allSuccessfulOrThrow()
Returns a new Joiner that produces a list of all results when all subtasks complete successfully. The `Joiner` cancels the scope and causes the `join()`PREVIEW method to throw `ExecutionException` if any subtask fails.

The `join()`PREVIEW method of a `StructuredTaskScope`PREVIEW opened with this Joiner returns the list of the results, in the order that the subtasks were forkedPREVIEW, when all subtasks complete successfully. An empty list is returned if no subtasks were forked. If any subtask fails then the Joiner causes the `join()` method to throw `ExecutionException` with the exception from the first subtask to fail as the cause.

Timeout Handling: The `Joiner` cannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeoutPREVIEW, and the timeout expires before or while waiting for all subtasks to complete successfully, then the `Joiner` causes the `join()` method to throw `ExecutionException` with a `CancelledByTimeoutException`PREVIEW as the cause.

Implementation Requirements:
  A Joiner returned by this method is equivalent to invoking `allSuccessfulOrThrow(ExecutionException::new)`.
Type Parameters:
  `T` - the result type of subtasks
Returns:
  a new Joiner that produces a list of all results when all subtasks complete successfully

### anySuccessfulOrThrow

static <T, R_X extends Throwable> StructuredTaskScope.JoinerPREVIEW<T,T,R_X> anySuccessfulOrThrow(Function<Throwable, R_X> esf)
Returns a new Joiner that produces the result of any successful subtask. The `Joiner` cancels the scope when a subtask completes successfully. It causes the `join()`PREVIEW method to throw if all subtasks fail.

The `join()`PREVIEW method of a `StructuredTaskScope`PREVIEW opened with this Joiner returns the result of a successful subtask. If a subtask completes successfully then the scope is cancelled and the `join()` method returns its result. If all subtasks fail then the Joiner causes the `join()` method to throw the exception returned by the given exception supplying function when applied to the exception from one of the failed subtasks. The function should return an exception with the exception from the failed subtask (the function argument) as the cause. If the function returns `null` then it causes the `join()` method to throw `NullPointerException`. If no subtasks were forked then the Joiner causes the `join()` method to throw the exception returned by the function when applied to an instance of `NoSuchElementException`.

Timeout Handling: The `Joiner` cannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeoutPREVIEW, and the timeout expires before or while waiting for a subtask to complete successfully, then the `Joiner` causes the `join()` method to throw the exception returned by the exception supplying function when applied to a `CancelledByTimeoutException`PREVIEW.

API Note:
  The following example is a method that opens a `StructuredTaskScope` with a Joiner created with `anySuccessfulOrThrow(Function)`. The method is invoked with a collection of callables. It forksPREVIEW a subtask to execute each callable, waits in `join()`PREVIEW for any subtask to complete successfully, returning its result. It throws the runtime exception `CompletionException` if all subtasks fail, with the exception from a failed subtask as the cause.

        <T> T invokeAny(Collection<Callable<T>> tasks) throws InterruptedException {
              try (var scope = StructuredTaskScope.open(
                    Joiner.<T, CompletionException>anySuccessfulOrThrow(CompletionException::new))) {
                  tasks.forEach(scope::fork);
                  T result = scope.join();
                  return result;
              }
        }

Type Parameters:
  `T` - the result type of subtasks
  `R_X` - the type of the exception thrown by the `join()`PREVIEW method
Parameters:
  `esf` - the exception supplying function
Returns:
  a new Joiner that produces the result of any successful subtask
Since:
  27
See Also:
  - `anySuccessfulOrThrow()`

### anySuccessfulOrThrow

static <T> StructuredTaskScope.JoinerPREVIEW<T, T, ExecutionException> anySuccessfulOrThrow()
Returns a new Joiner that produces the result of any successful subtask. The `Joiner` cancels the scope when a subtask completes successfully. It causes the `join()`PREVIEW method to throw `ExecutionException` if all subtasks fail.

The `join()`PREVIEW method of a `StructuredTaskScope`PREVIEW opened with this Joiner returns the result of a successful subtask. If a subtask completes successfully then the scope is cancelled and the `join()` method returns its result. If all subtasks fail then the Joiner causes the `join()` method to throw `ExecutionException` with the exception from one of the failed subtasks as the cause. If no subtasks were forked then the `Joiner` causes the `join()` method to throw `ExecutionException` with `NoSuchElementException` as the cause.

Timeout Handling: The `Joiner` cannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeoutPREVIEW, and the timeout expires before or while waiting for a subtask to complete successfully, then the `Joiner` causes the `join()` method to throw `ExecutionException` with a `CancelledByTimeoutException`PREVIEW as the cause.

Implementation Requirements:
  A Joiner returned by this method is equivalent to invoking `anySuccessfulOrThrow(ExecutionException::new)`.
Type Parameters:
  `T` - the result type of subtasks
Returns:
  a new Joiner that produces the result of any successful subtask
Since:
  26

### awaitAllSuccessfulOrThrow

static <T, R_X extends Throwable> StructuredTaskScope.JoinerPREVIEW<T,Void,R_X> awaitAllSuccessfulOrThrow(Function<Throwable, R_X> esf)
Returns a new Joiner that causes the `join()`PREVIEW method to wait for all subtasks to complete successfully. The `Joiner` cancels the scope and causes the `join()`PREVIEW method to throw if any subtask fails.

The `join()`PREVIEW method of a `StructuredTaskScope`PREVIEW opened with this Joiner returns `null` when all subtasks complete successfully. If any subtask fails then the Joiner causes the `join()` method to throw the exception returned by the given exception supplying function when applied to the exception from the first subtask to fail. The function should return an exception with the exception from the failed subtask (the function argument) as the cause. If the function returns `null` then it causes the `join()` method to throw `NullPointerException`.

Timeout Handling: The `Joiner` cannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeoutPREVIEW, and the timeout expires before or while waiting for all subtasks to complete successfully, then the `Joiner` causes the `join()` method to throw the exception returned by the exception supplying function when applied to a `CancelledByTimeoutException`PREVIEW.

API Note:
  Joiners returned by this method are suited to cases where subtasks return results of different types and where it is necessary to keep a reference to each `Subtask`PREVIEW. Joiners returned by `allSuccessfulOrThrow(Function)` and `allSuccessfulOrThrow()` are suited to cases where the subtasks return a result of the same type.

  The following example opens a `StructuredTaskScope` with a Joiner created with `awaitAllSuccessfulOrThrow()`. It forksPREVIEW two subtasks, then waits in `join()`PREVIEW for both subtasks to complete successfully or either subtask to fail. If both subtasks complete successfully then it invokes `Subtask.get()`PREVIEW on both subtasks to get their results. It throws the runtime exception `CompletionException` if any subtask fails, with the exception from a failed subtask as the cause.

        try (var scope = StructuredTaskScope.open(
                 Joiner.awaitAllSuccessfulOrThrow(CompletionException::new))) {
            Subtask<String> subtask1 = scope.fork(callable1);
            Subtask<Integer> subtask2 = scope.fork(callable2);

            // throws CompletionException if either subtask fails
            scope.join();

            // both subtasks completed successfully
            var result = new MyResult(subtask1.get(), subtask2.get());
        }

Type Parameters:
  `T` - the result type of subtasks
  `R_X` - the type of the exception thrown by the `join()`PREVIEW method
Parameters:
  `esf` - the exception supplying function
Returns:
  a new Joiner that causes the `join()`PREVIEW method to wait for all subtasks to complete successfully
Since:
  27

### awaitAllSuccessfulOrThrow

static <T> StructuredTaskScope.JoinerPREVIEW<T, Void, ExecutionException> awaitAllSuccessfulOrThrow()
Returns a new Joiner that causes the `join()`PREVIEW method to wait for all subtasks to complete successfully. The `Joiner` cancels the scope and causes the `join()`PREVIEW method to throw `ExecutionException` if any subtask fails.

The `join()`PREVIEW method of a `StructuredTaskScope`PREVIEW opened with this Joiner returns `null` when all subtasks complete successfully. If any subtask fails then the Joiner causes the `join()` method to throw `ExecutionException` with the exception from the first subtask to fail as the cause.

Timeout Handling: The `Joiner` cannot produce a result when the scope is cancelled by a timeout. If the scope was opened with a timeoutPREVIEW, and the timeout expires before or while waiting for all subtasks to complete successfully, then the `Joiner` causes the `join()` method to throw `ExecutionException` with a `CancelledByTimeoutException`PREVIEW as the cause.

Implementation Requirements:
  A Joiner returned by this method is equivalent to invoking `awaitAllSuccessfulOrThrow(ExecutionException::new)`.
Type Parameters:
  `T` - the result type of subtasks
Returns:
  a new Joiner that causes the `join()`PREVIEW method to wait for all subtasks to complete successfully
See Also:
  - `StructuredTaskScope.open()`PREVIEW
  - `StructuredTaskScope.open(UnaryOperator)`PREVIEW

### allUntil

static <T> StructuredTaskScope.JoinerPREVIEW<T, List<StructuredTaskScope.SubtaskPREVIEW<T>>, RuntimeException> allUntil(Predicate<? super StructuredTaskScope.SubtaskPREVIEW<T>> isDone)
Returns a new Joiner that produces a list of all subtasks when all subtasks complete or evaluating a predicate on a completed subtask causes the scope to be cancelled. The `Joiner` does not cause the `join()`PREVIEW method to throw if subtasks fail or a configured timeoutPREVIEW expires. This method can be used to create a `Joiner` that implements a cancellation policy.

The `join()`PREVIEW method of a `StructuredTaskScope`PREVIEW opened with this Joiner returns a list of all subtasks, in the order that they were forkedPREVIEW, when all subtasks complete or the scope is cancelled. The returned list may contain subtasks that completed (in the `SUCCESS`PREVIEW or `FAILED`PREVIEW state), or subtasks in the `UNAVAILABLE`PREVIEW state if they were forked or completed after the scope was cancelled. The scope is cancelled if the given predicate evaluates to `true`, or a configured timeout expires before or while waiting in the `join()` method.

The given `Predicate`'s `test(Object)` method is invoked on a completed subtask by the thread that executed the subtask. The method is invoked after the subtask completes (successfully or with an exception) before the thread terminates. The scope is cancelled if the `test` method returns `true`. The `test` method must be thread safe. It may be invoked concurrently from several threads as multiple subtasks can complete at the same time. If the method throws an exception or error, the thread invokes the uncaught exception handler with the exception or error before the thread terminates.

Timeout Handling: If used with a scope that has a timeoutPREVIEW set, and the timeout expires before all subtasks complete then the `join()` method returns the list of all subtasks. It does not throw an exception. Subtasks that did not complete before the timeout expires will be in the `UNAVAILABLE` state.

API Note:
  The following example uses `allUntil` to create a Joiner that cancels the scope when two or more subtasks fail.

         class CancelAfterTwoFailures implements Predicate<Subtask<?>> {
              private final AtomicInteger failedCount = new AtomicInteger();
              @Override
              public boolean test(Subtask<?> subtask) {
                  return subtask.state() == Subtask.State.FAILED
                          && failedCount.incrementAndGet() >= 2;
              }
          }

          var joiner = Joiner.<String>allUntil(new CancelAfterTwoFailures());

  The following example uses `allUntil` to create a Joiner that cancels the scope when any subtask completes successfully. The subtasks are grouped according to their state to produce a map with up to three key-value mappings. The map key is the subtask state, the value is the list of subtasks in that state.

          Predicate<Subtask<?>> isSuccessful = s -> s.state() == Subtask.State.SUCCESS;

          try (var scope = StructuredTaskScope.open(Joiner.<String>allUntil(isSuccessful))) {
              tasks.forEach(scope::fork);

              Map<Subtask.State, List<Subtask<String>>> subtasksByState = scope.join()
                      .stream()
                      .collect(Collectors.groupingBy(Subtask::state));
          }

  The following example is a method that uses `allUntil` to create a Joiner that does not cancel the scope. The method waits for all subtasks to complete or a timeout to expire. It returns a list of all subtasks, in the same order as the collection of callables, even if the timeout expires before or while waiting in `join()`.

         <T> List<Subtask<T>> invokeAll(Collection<Callable<T>> tasks, Duration timeout) throws InterruptedException {
             try (var scope = StructuredTaskScope.open(Joiner.<T>allUntil(_ -> false), cf -> cf.withTimeout(timeout))) {
                 tasks.forEach(scope::fork);
                 return scope.join();
             }
         }

Type Parameters:
  `T` - the result type of subtasks
Parameters:
  `isDone` - the predicate to evaluate completed subtasks
Returns:
  a new Joiner that produces a list of all subtasks when all subtasks complete or evaluating a predicate on a completed subtask causes the scope to be cancelled