add orDefault methods to promises & more factory overloads

This commit is contained in:
tommyskeff
2025-01-09 09:59:19 +00:00
parent e10db36a63
commit df9e418091
11 changed files with 346 additions and 247 deletions

View File

@@ -5,7 +5,6 @@ import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import dev.tommyjs.futur.util.ConcurrentResultArray;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.List;
@@ -36,9 +35,8 @@ public class CompletionJoiner extends PromiseJoiner<Promise<?>, Void, Void, List
}
@Override
protected @Nullable Throwable onChildComplete(int index, Void key, @NotNull PromiseCompletion<Void> res) {
protected void onChildComplete(int index, Void key, @NotNull PromiseCompletion<Void> res) {
results.set(index, res);
return null;
}
@Override

View File

@@ -5,24 +5,19 @@ import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import dev.tommyjs.futur.util.ConcurrentResultArray;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiConsumer;
public class MappedResultJoiner<K, V> extends PromiseJoiner<Map.Entry<K, Promise<V>>, K, V, Map<K, V>> {
private final @Nullable BiConsumer<K, Throwable> exceptionHandler;
private final @NotNull ConcurrentResultArray<Map.Entry<K, V>> results;
public MappedResultJoiner(
@NotNull PromiseFactory factory,
@NotNull Iterator<Map.Entry<K, Promise<V>>> promises,
@Nullable BiConsumer<K, Throwable> exceptionHandler,
int expectedSize, boolean link
) {
super(factory);
this.exceptionHandler = exceptionHandler;
this.results = new ConcurrentResultArray<>(expectedSize);
join(promises, link);
}
@@ -38,17 +33,8 @@ public class MappedResultJoiner<K, V> extends PromiseJoiner<Map.Entry<K, Promise
}
@Override
protected @Nullable Throwable onChildComplete(int index, K key, @NotNull PromiseCompletion<V> res) {
if (res.isError()) {
if (exceptionHandler == null) {
return res.getException();
}
exceptionHandler.accept(key, res.getException());
}
protected void onChildComplete(int index, K key, @NotNull PromiseCompletion<V> res) {
results.set(index, new AbstractMap.SimpleImmutableEntry<>(key, res.getResult()));
return null;
}
@Override

View File

@@ -6,7 +6,6 @@ import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import dev.tommyjs.futur.util.PromiseUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;
@@ -23,7 +22,7 @@ public abstract class PromiseJoiner<V, K, T, R> {
protected abstract @NotNull Promise<T> getChildPromise(V value);
protected abstract @Nullable Throwable onChildComplete(int index, K key, @NotNull PromiseCompletion<T> completion);
protected abstract void onChildComplete(int index, K key, @NotNull PromiseCompletion<T> completion);
protected abstract R getResult();
@@ -45,9 +44,10 @@ public abstract class PromiseJoiner<V, K, T, R> {
int index = i++;
p.addAsyncListener(res -> {
Throwable e = onChildComplete(index, key, res);
if (e != null) {
joined.completeExceptionally(e);
onChildComplete(index, key, res);
if (res.isError()) {
assert res.getException() != null;
joined.completeExceptionally(res.getException());
} else if (count.decrementAndGet() == -1) {
joined.complete(getResult());
}

View File

@@ -5,25 +5,20 @@ import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import dev.tommyjs.futur.util.ConcurrentResultArray;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiConsumer;
public class ResultJoiner<T> extends PromiseJoiner<Promise<T>, Void, T, List<T>> {
private final @Nullable BiConsumer<Integer, Throwable> exceptionHandler;
private final ConcurrentResultArray<T> results;
public ResultJoiner(
@NotNull PromiseFactory factory,
@NotNull Iterator<Promise<T>> promises,
@Nullable BiConsumer<Integer, Throwable> exceptionHandler,
int expectedSize, boolean link
) {
super(factory);
this.exceptionHandler = exceptionHandler;
this.results = new ConcurrentResultArray<>(expectedSize);
join(promises, link);
}
@@ -39,17 +34,8 @@ public class ResultJoiner<T> extends PromiseJoiner<Promise<T>, Void, T, List<T>>
}
@Override
protected @Nullable Throwable onChildComplete(int index, Void key, @NotNull PromiseCompletion<T> res) {
if (res.isError()) {
if (exceptionHandler == null) {
return res.getException();
}
exceptionHandler.accept(index, res.getException());
}
protected void onChildComplete(int index, Void key, @NotNull PromiseCompletion<T> res) {
results.set(index, res.getResult());
return null;
}
@Override

View File

@@ -4,7 +4,6 @@ import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
@@ -27,8 +26,8 @@ public class VoidJoiner extends PromiseJoiner<Promise<?>, Void, Void, Void> {
}
@Override
protected @Nullable Throwable onChildComplete(int index, Void key, @NotNull PromiseCompletion<Void> completion) {
return completion.getException();
protected void onChildComplete(int index, Void key, @NotNull PromiseCompletion<Void> completion) {
}
@Override

View File

@@ -11,7 +11,10 @@ import org.slf4j.Logger;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
@@ -355,7 +358,7 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
@Override
public @NotNull Promise<T> thenPopulateReference(@NotNull AtomicReference<T> reference) {
return thenApplyAsync(result -> {
return thenApply(result -> {
reference.set(result);
return result;
});
@@ -400,7 +403,7 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
private @NotNull Promise<T> addAnyListener(PromiseListener<T> listener) {
Collection<PromiseListener<T>> prev = listeners, next = null;
for (boolean haveNext = false;;) {
for (boolean haveNext = false; ; ) {
if (!haveNext) {
next = prev == Collections.EMPTY_LIST ? new ConcurrentLinkedQueue<>() : prev;
if (next != null) next.add(listener);
@@ -474,6 +477,32 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
return onError(CancellationException.class, listener);
}
@Override
public @NotNull Promise<T> orDefault(@Nullable T defaultValue) {
return orDefault(_ -> defaultValue);
}
@Override
public @NotNull Promise<T> orDefault(@NotNull ExceptionalSupplier<T> supplier) {
return orDefault(_ -> supplier.get());
}
@Override
public @NotNull Promise<T> orDefault(@NotNull ExceptionalFunction<Throwable, T> function) {
CompletablePromise<T> promise = getFactory().unresolved();
addDirectListener(promise::complete, e -> {
try {
T result = function.apply(e);
promise.complete(result);
} catch (Exception ex) {
promise.completeExceptionally(ex);
}
});
PromiseUtil.propagateCancel(promise, this);
return promise;
}
@Override
public @NotNull Promise<T> timeout(long time, @NotNull TimeUnit unit) {
Exception e = new CancellationException("Promise timed out after " + time + " " + unit.toString().toLowerCase());
@@ -517,7 +546,8 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
private void callListenerAsyncLastResort(PromiseListener<T> listener, PromiseCompletion<T> completion) {
try {
getFactory().getAsyncExecutor().run(() -> callListenerNow(listener, completion));
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
}
@Override
@@ -582,7 +612,7 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
}
c2 = c1 - 1;
} while(!compareAndSetState(c1, c2));
} while (!compareAndSetState(c1, c2));
return c2 == 0;
}

View File

@@ -7,14 +7,12 @@ import dev.tommyjs.futur.joiner.ResultJoiner;
import dev.tommyjs.futur.joiner.VoidJoiner;
import dev.tommyjs.futur.util.PromiseUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Future;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
public abstract class AbstractPromiseFactory<FS, FA> implements PromiseFactory {
@@ -71,24 +69,18 @@ public abstract class AbstractPromiseFactory<FS, FA> implements PromiseFactory {
}
@Override
public <K, V> @NotNull Promise<Map<K, V>> combine(
@NotNull Map<K, Promise<V>> promises,
@Nullable BiConsumer<K, Throwable> exceptionHandler,
boolean link
) {
if (promises.isEmpty()) return resolve(Collections.emptyMap());
return new MappedResultJoiner<>(this,
promises.entrySet().iterator(), exceptionHandler, promises.size(), link).joined();
public @NotNull <K, V> Promise<Map<K, V>> combineMapped(@NotNull Iterator<Map.Entry<K, Promise<V>>> promises, int expectedSize, boolean link) {
if (!promises.hasNext()) return resolve(Collections.emptyMap());
return new MappedResultJoiner<>(this, promises, expectedSize, link).joined();
}
@Override
public <V> @NotNull Promise<List<V>> combine(
@NotNull Iterator<Promise<V>> promises, int expectedSize,
@Nullable BiConsumer<Integer, Throwable> exceptionHandler, boolean link
@NotNull Iterator<Promise<V>> promises,
int expectedSize, boolean link
) {
if (!promises.hasNext()) return resolve(Collections.emptyList());
return new ResultJoiner<>(
this, promises, exceptionHandler, expectedSize, link).joined();
return new ResultJoiner<>(this, promises, expectedSize, link).joined();
}
@Override

View File

@@ -430,6 +430,43 @@ public interface Promise<T> {
*/
@NotNull Promise<T> onCancel(@NotNull Consumer<CancellationException> listener);
/**
* Creates a new promise that will always complete successfully - either with the result of this
* promise, or with the specified default value if this promise completes exceptionally. Cancelling
* the returned promise will cancel this promise, and consequently any previous promises in the chain.
*
* @param defaultValue the default value to complete the promise with if this promise completes exceptionally
* @return a new promise that completes with the result of this promise, or with the default value if this
* promise completes exceptionally
*/
@NotNull Promise<T> orDefault(@Nullable T defaultValue);
/**
* Creates a new promise that will attempt to always complete successfully - either with the result
* of this promise, or with the result of the specified supplier if this promise completes exceptionally.
* If an exception is encountered while executing the supplier, the promise will complete exceptionally
* with that exception. Cancelling the returned promise will cancel this promise, and consequently any
* previous promises in the chain.
*
* @param supplier the supplier to complete the promise with if this promise completes exceptionally
* @return a new promise that completes with the result of this promise, or with the result of the
* supplier if this promise completes exceptionally
*/
@NotNull Promise<T> orDefault(@NotNull ExceptionalSupplier<T> supplier);
/**
* Creates a new promise that will attempt to always complete successfully - either with the result
* of this promise, or with the result of the specified function if this promise completes
* exceptionally. If an exception is encountered while executing the function, the promise will
* complete exceptionally with that exception. Cancelling the returned promise will cancel this
* promise, and consequently any previous promises in the chain.
*
* @param function the function to complete the promise with if this promise completes exceptionally
* @return a new promise that completes with the result of this promise, or with the result of the
* function if this promise completes exceptionally
*/
@NotNull Promise<T> orDefault(@NotNull ExceptionalFunction<Throwable, T> function);
/**
* Cancels the promise if not already completed after the specified timeout. This will result in
* an exceptional completion with a {@link CancellationException}.

View File

@@ -3,14 +3,14 @@ package dev.tommyjs.futur.promise;
import dev.tommyjs.futur.executor.PromiseExecutor;
import dev.tommyjs.futur.util.PromiseUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public interface PromiseFactory {
@@ -122,32 +122,26 @@ public interface PromiseFactory {
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If {@code link} is {@code true}
* and any promise completes exceptionally, the other promises will be cancelled and the output
* promise will complete exceptionally. If an exception handler is present, promises that fail
* will not cause this behaviour, and instead the exception handler will be called with the key
* that failed and the exception.
* promise will complete exceptionally.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
<K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises,
@Nullable BiConsumer<K, Throwable> exceptionHandler,
boolean link);
<K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Iterator<Map.Entry<K, Promise<V>>> promises,
int expectedSize, boolean link);
/**
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If any promise completes exceptionally,
* the exception handler will be called with the key that failed and the exception. The output promise
* will always complete successfully regardless of whether input promises fail.
* the output promise will complete exceptionally.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises,
@NotNull BiConsumer<K, Throwable> exceptionHandler) {
return combine(promises, exceptionHandler, true);
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Collection<Map.Entry<K, Promise<V>>> promises,
boolean link) {
return combineMapped(promises.iterator(), promises.size(), link);
}
/**
@@ -160,8 +154,9 @@ public interface PromiseFactory {
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises, boolean link) {
return combine(promises, null, link);
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Map<K, Promise<V>> promises,
boolean link) {
return combineMapped(promises.entrySet().iterator(), promises.size(), link);
}
/**
@@ -172,8 +167,89 @@ public interface PromiseFactory {
* @param promises the input promises
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Map<K, Promise<V>> promises) {
return combineMapped(promises, true);
}
/**
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If any promise completes exceptionally,
* the output promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Collection<Map.Entry<K, Promise<V>>> promises) {
return combineMapped(promises, true);
}
/**
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If {@code link} is {@code true}
* and any promise completes exceptionally, the other promises will be cancelled and the output
* promise will complete exceptionally.
*
* @param promises the input promises
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Stream<Map.Entry<K, Promise<V>>> promises,
boolean link) {
return combineMapped(promises.iterator(), PromiseUtil.estimateSize(promises), link);
}
/**
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If any promise completes exceptionally,
* the output promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Stream<Map.Entry<K, Promise<V>>> promises) {
return combineMapped(promises, true);
}
/**
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If {@code link} is {@code true}
* and any promise completes exceptionally, the other promises will be cancelled and the output
* promise will complete exceptionally.
*
* @param keys the input keys
* @param mapper the function to map keys to value promises
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Iterable<K> keys,
@NotNull Function<K, Promise<V>> mapper,
boolean link) {
return combineMapped(StreamSupport.stream(keys.spliterator(), true)
.map(k -> new AbstractMap.SimpleImmutableEntry<>(k, mapper.apply(k))), link);
}
/**
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If any promise completes exceptionally,
* the output promise will complete exceptionally.
*
* @param keys the input keys
* @param mapper the function to map keys to value promises
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Iterable<K> keys,
@NotNull Function<K, Promise<V>> mapper) {
return combineMapped(keys, mapper, true);
}
@Deprecated
default <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises, boolean link) {
return combineMapped(promises, link);
}
@Deprecated
default <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises) {
return combine(promises, null, true);
return combineMapped(promises);
}
/**
@@ -184,45 +260,12 @@ public interface PromiseFactory {
* handler will be called with the index that failed and the exception.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
<V> @NotNull Promise<List<V>> combine(@NotNull Iterator<Promise<V>> promises, int expectedSize,
@Nullable BiConsumer<Integer, Throwable> exceptionHandler,
boolean link);
/**
* Combines a collection of promises into a single promise that completes with a list of results when all
* promises complete. If any promise completes exceptionally, the exception handler will be called with
* the index that failed and the exception. The output promise will always complete successfully regardless
* of whether input promises fail.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @return the combined promise
*/
default <V> @NotNull Promise<List<V>> combine(@NotNull Collection<Promise<V>> promises,
@NotNull BiConsumer<Integer, Throwable> exceptionHandler,
boolean link) {
return combine(promises.iterator(), promises.size(), exceptionHandler, link);
}
/**
* Combines a collection of promises into a single promise that completes with a list of results when all
* promises complete. If any promise completes exceptionally, the exception handler will be called with
* the index that failed and the exception. The output promise will always complete successfully regardless
* of whether input promises fail.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @return the combined promise
*/
default <V> @NotNull Promise<List<V>> combine(@NotNull Collection<Promise<V>> promises,
@NotNull BiConsumer<Integer, Throwable> exceptionHandler) {
return combine(promises.iterator(), promises.size(), exceptionHandler, true);
}
/**
* Combines a collection of promises into a single promise that completes with a list of results when all
* promises complete. If {@code link} is {@code true} and any promise completes exceptionally, all
@@ -233,7 +276,7 @@ public interface PromiseFactory {
* @return the combined promise
*/
default <V> @NotNull Promise<List<V>> combine(@NotNull Collection<Promise<V>> promises, boolean link) {
return combine(promises.iterator(), promises.size(), null, link);
return combine(promises.iterator(), promises.size(), link);
}
/**
@@ -244,7 +287,7 @@ public interface PromiseFactory {
* @return the combined promise
*/
default <V> @NotNull Promise<List<V>> combine(@NotNull Collection<Promise<V>> promises) {
return combine(promises.iterator(), promises.size(), null, true);
return combine(promises, true);
}
/**
@@ -255,53 +298,23 @@ public interface PromiseFactory {
* handler will be called with the index that failed and the exception.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
default <V> @NotNull Promise<List<V>> combine(@NotNull Stream<Promise<V>> promises,
@NotNull BiConsumer<Integer, Throwable> exceptionHandler,
boolean link) {
return combine(promises.iterator(), PromiseUtil.estimateSize(promises), exceptionHandler, link);
}
/**
* Combines a stream of promises into a single promise that completes with a list of results when all
* promises complete. If any promise completes exceptionally, the exception handler will be called with
* the index that failed and the exception. The output promise will always complete successfully regardless
* of whether input promises fail.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @return the combined promise
*/
default <V> @NotNull Promise<List<V>> combine(@NotNull Stream<Promise<V>> promises,
@NotNull BiConsumer<Integer, Throwable> exceptionHandler) {
return combine(promises.iterator(), PromiseUtil.estimateSize(promises), exceptionHandler, true);
}
/**
* Combines a stream of promises into a single promise that completes with a list of results when all
* promises complete. If {@code link} is {@code true} and any promise completes exceptionally, all
* other promises will be cancelled and the output promise will complete exceptionally.
*
* @param promises the input promises
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
default <V> @NotNull Promise<List<V>> combine(@NotNull Stream<Promise<V>> promises, boolean link) {
return combine(promises.iterator(), PromiseUtil.estimateSize(promises), null, link);
return combine(promises.iterator(), PromiseUtil.estimateSize(promises), link);
}
/**
* Combines a stream of promises into a single promise that completes with a list of results when all
* promises complete. If any promise completes exceptionally, the output promise will complete exceptionally.
* promises complete. The output promise will always complete successfully regardless of whether input
* promises fail.
*
* @param promises the input promises
* @return the combined promise
*/
default <V> @NotNull Promise<List<V>> combine(@NotNull Stream<Promise<V>> promises) {
return combine(promises.iterator(), PromiseUtil.estimateSize(promises), null, true);
return combine(promises.iterator(), PromiseUtil.estimateSize(promises), true);
}
/**

View File

@@ -1,5 +1,6 @@
package dev.tommyjs.futur;
import dev.tommyjs.futur.promise.CompletablePromise;
import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseFactory;
import org.junit.jupiter.api.Test;
@@ -10,6 +11,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public final class PromiseTests {
@@ -96,7 +98,7 @@ public final class PromiseTests {
)
.get(100L, TimeUnit.MILLISECONDS);
promises.combine(
promises.combineMapped(
Map.of(
"a", promises.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS),
"b", promises.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS)
@@ -139,7 +141,7 @@ public final class PromiseTests {
.cancel();
var finished5 = new AtomicBoolean();
promises.combine(
promises.combineMapped(
Map.of(
"a", promises.start().thenRunDelayedAsync(() -> finished5.set(true), 50, TimeUnit.MILLISECONDS),
"b", promises.start().thenRunDelayedAsync(() -> finished5.set(true), 50, TimeUnit.MILLISECONDS)
@@ -165,4 +167,47 @@ public final class PromiseTests {
).await();
}
@Test
public void testOrDefault() {
CompletablePromise<Integer> promise = promises.unresolved();
AtomicReference<Integer> res = new AtomicReference<>();
promise.orDefault(10).thenPopulateReference(res);
promise.completeExceptionally(new IllegalStateException("Test"));
assert res.get() == 10;
}
@Test
public void testOrDefaultSupplier() {
CompletablePromise<Integer> promise = promises.unresolved();
AtomicReference<Integer> res = new AtomicReference<>();
promise.orDefault(() -> 10).thenPopulateReference(res);
promise.completeExceptionally(new IllegalStateException("Test"));
assert res.get() == 10;
}
@Test
public void testOrDefaultFunction() {
CompletablePromise<Integer> promise = promises.unresolved();
AtomicReference<Integer> res = new AtomicReference<>();
promise.orDefault(e -> {
assert e instanceof IllegalStateException;
return 10;
}).thenPopulateReference(res);
promise.completeExceptionally(new IllegalStateException("Test"));
assert res.get() == 10;
}
@Test
public void testOrDefaultError() {
CompletablePromise<Integer> promise = promises.unresolved();
AtomicReference<Integer> res = new AtomicReference<>();
Promise<Integer> promise2 = promise.orDefault(e -> {
throw new IllegalStateException("Test");
}).thenPopulateReference(res);
promise.completeExceptionally(new IllegalStateException("Test"));
assert res.get() == null;
assert promise2.getCompletion() != null && promise2.getCompletion().getException() instanceof IllegalStateException;
}
}

View File

@@ -16,6 +16,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Stream;
public final class Promises {
@@ -112,34 +113,28 @@ public final class Promises {
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If {@code link} is {@code true}
* and any promise completes exceptionally, the other promises will be cancelled and the output
* promise will complete exceptionally. If an exception handler is present, promises that fail
* will not cause this behaviour, and instead the exception handler will be called with the key
* that failed and the exception.
* promise will complete exceptionally.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises,
@Nullable BiConsumer<K, Throwable> exceptionHandler,
boolean link) {
return factory.combine(promises, exceptionHandler, link);
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Iterator<Map.Entry<K, Promise<V>>> promises,
int expectedSize, boolean link) {
return factory.combineMapped(promises, expectedSize, link);
}
/**
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If any promise completes exceptionally,
* the exception handler will be called with the key that failed and the exception. The output promise
* will always complete successfully regardless of whether input promises fail.
* the output promise will complete exceptionally.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises,
@NotNull BiConsumer<K, Throwable> exceptionHandler) {
return factory.combine(promises, exceptionHandler);
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Collection<Map.Entry<K, Promise<V>>> promises,
boolean link) {
return factory.combineMapped(promises, link);
}
/**
@@ -152,8 +147,9 @@ public final class Promises {
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises, boolean link) {
return factory.combine(promises, link);
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Map<K, Promise<V>> promises,
boolean link) {
return factory.combineMapped(promises, link);
}
/**
@@ -164,6 +160,86 @@ public final class Promises {
* @param promises the input promises
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Map<K, Promise<V>> promises) {
return factory.combineMapped(promises);
}
/**
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If any promise completes exceptionally,
* the output promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Collection<Map.Entry<K, Promise<V>>> promises) {
return factory.combineMapped(promises);
}
/**
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If {@code link} is {@code true}
* and any promise completes exceptionally, the other promises will be cancelled and the output
* promise will complete exceptionally.
*
* @param promises the input promises
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Stream<Map.Entry<K, Promise<V>>> promises,
boolean link) {
return factory.combineMapped(promises, link);
}
/**
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If any promise completes exceptionally,
* the output promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Stream<Map.Entry<K, Promise<V>>> promises) {
return factory.combineMapped(promises);
}
/**
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If {@code link} is {@code true}
* and any promise completes exceptionally, the other promises will be cancelled and the output
* promise will complete exceptionally.
*
* @param keys the input keys
* @param mapper the function to map keys to value promises
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Iterable<K> keys,
@NotNull Function<K, Promise<V>> mapper,
boolean link) {
return factory.combineMapped(keys, mapper, link);
}
/**
* Combines key-value pairs of inputs to promises into a single promise that completes with key-value
* pairs of inputs to outputs when all promises complete. If any promise completes exceptionally,
* the output promise will complete exceptionally.
*
* @param keys the input keys
* @param mapper the function to map keys to value promises
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Iterable<K> keys,
@NotNull Function<K, Promise<V>> mapper) {
return factory.combineMapped(keys, mapper);
}
@Deprecated
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises, boolean link) {
return factory.combine(promises, link);
}
@Deprecated
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises) {
return factory.combine(promises);
}
@@ -176,45 +252,12 @@ public final class Promises {
* handler will be called with the index that failed and the exception.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
public static <V> @NotNull Promise<List<V>> combine(@NotNull Iterator<Promise<V>> promises, int expectedSize,
@Nullable BiConsumer<Integer, Throwable> exceptionHandler,
boolean link) {
return factory.combine(promises, expectedSize, exceptionHandler, link);
}
/**
* Combines a collection of promises into a single promise that completes with a list of results when all
* promises complete. If any promise completes exceptionally, the exception handler will be called with
* the index that failed and the exception. The output promise will always complete successfully regardless
* of whether input promises fail.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @return the combined promise
*/
public static <V> @NotNull Promise<List<V>> combine(@NotNull Collection<Promise<V>> promises,
@NotNull BiConsumer<Integer, Throwable> exceptionHandler,
boolean link) {
return factory.combine(promises, exceptionHandler, link);
}
/**
* Combines a collection of promises into a single promise that completes with a list of results when all
* promises complete. If any promise completes exceptionally, the exception handler will be called with
* the index that failed and the exception. The output promise will always complete successfully regardless
* of whether input promises fail.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @return the combined promise
*/
public static <V> @NotNull Promise<List<V>> combine(@NotNull Collection<Promise<V>> promises,
@NotNull BiConsumer<Integer, Throwable> exceptionHandler) {
return factory.combine(promises, exceptionHandler);
return factory.combine(promises, expectedSize, link);
}
/**
@@ -249,37 +292,6 @@ public final class Promises {
* handler will be called with the index that failed and the exception.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
public static <V> @NotNull Promise<List<V>> combine(@NotNull Stream<Promise<V>> promises,
@NotNull BiConsumer<Integer, Throwable> exceptionHandler,
boolean link) {
return factory.combine(promises, exceptionHandler, link);
}
/**
* Combines a stream of promises into a single promise that completes with a list of results when all
* promises complete. If any promise completes exceptionally, the exception handler will be called with
* the index that failed and the exception. The output promise will always complete successfully regardless
* of whether input promises fail.
*
* @param promises the input promises
* @param exceptionHandler the exception handler
* @return the combined promise
*/
public static <V> @NotNull Promise<List<V>> combine(@NotNull Stream<Promise<V>> promises,
@NotNull BiConsumer<Integer, Throwable> exceptionHandler) {
return factory.combine(promises, exceptionHandler);
}
/**
* Combines a stream of promises into a single promise that completes with a list of results when all
* promises complete. If {@code link} is {@code true} and any promise completes exceptionally, all
* other promises will be cancelled and the output promise will complete exceptionally.
*
* @param promises the input promises
* @param link whether to cancel all promises on any exceptional completions
* @return the combined promise
*/
@@ -289,7 +301,8 @@ public final class Promises {
/**
* Combines a stream of promises into a single promise that completes with a list of results when all
* promises complete. If any promise completes exceptionally, the output promise will complete exceptionally.
* promises complete. The output promise will always complete successfully regardless of whether input
* promises fail.
*
* @param promises the input promises
* @return the combined promise