From 4aed358d576d2717b3ffb655ac2ec3a09483d5b3 Mon Sep 17 00:00:00 2001 From: Jerry Lee Date: Sat, 27 Apr 2024 18:22:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20implement=20`peek`=20methods=20?= =?UTF-8?q?=F0=9F=AB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/io/foldright/cffu/Cffu.java | 108 ++++++++++++++---- .../cffu/CompletableFutureUtils.java | 76 ++++++++++++ .../java/io/foldright/cffu/package-info.java | 2 +- .../test/java/io/foldright/cffu/CffuTest.java | 25 ++++ .../cffu/CompletableFutureUtilsTest.java | 29 +++++ .../kotlin/CompletableFutureExtensions.kt | 60 ++++++++++ 6 files changed, 279 insertions(+), 21 deletions(-) diff --git a/cffu-core/src/main/java/io/foldright/cffu/Cffu.java b/cffu-core/src/main/java/io/foldright/cffu/Cffu.java index 7865e603..c7adc647 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/Cffu.java +++ b/cffu-core/src/main/java/io/foldright/cffu/Cffu.java @@ -1315,8 +1315,9 @@ public Cffu completeOnTimeout(@Nullable T value, long timeout, TimeUnit unit) // - thenCompose*: T -> CompletionStage // - exceptionallyCompose*: throwable -> CompletionStage // - // - whenComplete*: (T, throwable) -> Void - // - handle*: (T, throwable) -> U + // - handle*: (T, throwable) -> U + // - whenComplete*: (T, throwable) -> Void + // - peek*: (T, throwable) -> Void // // NOTE about advanced meaning: // - `compose` methods, input function argument return CompletionStage @@ -1504,18 +1505,85 @@ public Cffu whenCompleteAsync(BiConsumer action * @return the new Cffu */ @Override - public Cffu whenCompleteAsync( - BiConsumer action, Executor executor) { + public Cffu whenCompleteAsync(BiConsumer action, Executor executor) { return reset0(cf.whenCompleteAsync(action, executor)); } /** - * Returns a new Cffu that, when this stage completes either normally or exceptionally, - * is executed with this stage's result and exception as arguments to the supplied function. + * Peeks the result by executing the given action when this cffu completes, returns this cffu. *

- * When this stage is complete, the given function is invoked with the result (or {@code null} if none) - * and the exception (or {@code null} if none) of this stage as arguments, - * and the function's result is used to complete the returned stage. + * When this cffu is complete, the given action is invoked with the result (or {@code null} if none) + * and the exception (or {@code null} if none) of this cffu as arguments. + *

+ * Unlike method {@link CompletionStage#handle handle} and like method + * {@link CompletionStage#whenComplete(BiConsumer) whenComplete}, + * this method is not designed to translate completion outcomes; + * whether the supplied action should not throw an exception or not, + * do NOT affect this cffu. + * + * @param action the action to perform + * @return this Cffu + * @see CompletionStage#whenComplete(BiConsumer) + * @see java.util.stream.Stream#peek(Consumer) + */ + public Cffu peek(BiConsumer action) { + cf.whenComplete(action); + return this; + } + + /** + * Peeks the result by executing the given action when this cffu completes, + * executes the given action using {@link #defaultExecutor()}, returns this cffu. + *

+ * When this cffu is complete, the given action is invoked with the result (or {@code null} if none) + * and the exception (or {@code null} if none) of this cffu as arguments. + *

+ * Unlike method {@link CompletionStage#handle handle} and like method + * {@link CompletionStage#whenComplete(BiConsumer) whenComplete}, + * this method is not designed to translate completion outcomes; + * whether the supplied action should not throw an exception or not, + * do NOT affect this cffu. + * + * @param action the action to perform + * @return this Cffu + * @see CompletionStage#whenCompleteAsync(BiConsumer) + * @see java.util.stream.Stream#peek(Consumer) + */ + public Cffu peekAsync(BiConsumer action) { + cf.whenCompleteAsync(action, fac.defaultExecutor()); + return this; + } + + /** + * Peeks the result by executing the given action when this cffu completes, + * that executes the given action using the supplied Executor when this cffu completes, returns this cffu. + *

+ * When this cffu is complete, the given action is invoked with the result (or {@code null} if none) + * and the exception (or {@code null} if none) of this cffu as arguments. + *

+ * Unlike method {@link CompletionStage#handle handle} and like method + * {@link CompletionStage#whenComplete(BiConsumer) whenComplete}, + * this method is not designed to translate completion outcomes; + * whether the supplied action should not throw an exception or not, + * do NOT affect this cffu. + * + * @param action the action to perform + * @return this Cffu + * @see CompletionStage#whenCompleteAsync(BiConsumer) + * @see java.util.stream.Stream#peek(Consumer) + */ + public Cffu peekAsync(BiConsumer action, Executor executor) { + cf.whenCompleteAsync(action, executor); + return this; + } + + /** + * Returns a new Cffu that, when this cffu completes either normally or exceptionally, + * is executed with this cffu's result and exception as arguments to the supplied function. + *

+ * When this cffu is complete, the given function is invoked with the result (or {@code null} if none) + * and the exception (or {@code null} if none) of this cffu as arguments, + * and the function's result is used to complete the returned cffu. * * @param fn the function to use to compute the value of the returned Cffu * @param the function's return type @@ -1528,13 +1596,13 @@ public Cffu handle(BiFunction fn) { } /** - * Returns a new Cffu that, when this stage completes either normally or exceptionally, + * Returns a new Cffu that, when this cffu completes either normally or exceptionally, * is executed using {@link #defaultExecutor()}, - * with this stage's result and exception as arguments to the supplied function. + * with this cffu's result and exception as arguments to the supplied function. *

- * When this stage is complete, the given function is invoked with the result (or {@code null} if none) - * and the exception (or {@code null} if none) of this stage as arguments, - * and the function's result is used to complete the returned stage. + * When this Cffu is complete, the given function is invoked with the result (or {@code null} if none) + * and the exception (or {@code null} if none) of this Cffu as arguments, + * and the function's result is used to complete the returned Cffu. * * @param fn the function to use to compute the value of the returned Cffu * @param the function's return type @@ -1547,15 +1615,15 @@ public Cffu handleAsync(BiFunction fn) } /** - * Returns a new Cffu that, when this stage completes either normally or exceptionally, - * is executed using the supplied executor, with this stage's result and exception + * Returns a new Cffu that, when this cffu completes either normally or exceptionally, + * is executed using the supplied executor, with this cffu's result and exception * as arguments to the supplied function. *

- * When this stage is complete, the given function is invoked with the result (or {@code null} if none) - * and the exception (or {@code null} if none) of this stage as arguments, - * and the function's result is used to complete the returned stage. + * When this cffu is complete, the given function is invoked with the result (or {@code null} if none) + * and the exception (or {@code null} if none) of this cffu as arguments, + * and the function's result is used to complete the returned cffu. * - * @param fn the function to use to compute the value of the returned Cffu + * @param fn the function to use to compute the value of the returned cffu * @param executor the executor to use for asynchronous execution * @param the function's return type * @return the new Cffu diff --git a/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java b/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java index a903b316..55bde0db 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java +++ b/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java @@ -928,6 +928,82 @@ public static CompletableFuture applyToEitherSuccessAsync( return anyOfSuccess(cf1.toCompletableFuture(), cf2.toCompletableFuture()).thenApplyAsync(fn, executor); } + //////////////////////////////////////////////////////////////////////////////// + //# More new enhanced methods + //////////////////////////////////////////////////////////////////////////////// + + /** + * Peeks the result by executing the given action when given stage completes, returns the given stage. + *

+ * When the given stage is complete, the given action is invoked with the result (or {@code null} if none) + * and the exception (or {@code null} if none) of given stage as arguments. + *

+ * Unlike method {@link CompletionStage#handle handle} and like method + * {@link CompletionStage#whenComplete(BiConsumer) whenComplete}, + * this method is not designed to translate completion outcomes; + * whether the supplied action should not throw an exception or not, + * do NOT affect the given stage. + * + * @param action the action to perform + * @return the given stage + * @see CompletionStage#whenComplete(BiConsumer) + * @see java.util.stream.Stream#peek(Consumer) + */ + public static > C peek( + C cf, BiConsumer action) { + cf.whenComplete(action); + return cf; + } + + /** + * Peeks the result by executing the given action when given stage completes, + * executes the given action using given stage's default asynchronous execution facility, + * returns the given stage. + *

+ * When the given stage is complete, the given action is invoked with the result (or {@code null} if none) + * and the exception (or {@code null} if none) of given stage as arguments. + *

+ * Unlike method {@link CompletionStage#handle handle} and like method + * {@link CompletionStage#whenComplete(BiConsumer) whenComplete}, + * this method is not designed to translate completion outcomes; + * whether the supplied action should not throw an exception or not, + * do NOT affect the given stage. + * + * @param action the action to perform + * @return the given stage + * @see CompletionStage#whenCompleteAsync(BiConsumer) + * @see java.util.stream.Stream#peek(Consumer) + */ + public static > C peekAsync( + C cf, BiConsumer action) { + cf.whenCompleteAsync(action); + return cf; + } + + /** + * Peeks the result by executing the given action when given stage completes, + * executes the given action using the supplied Executor, returns the given stage. + *

+ * When the given stage is complete, the given action is invoked with the result (or {@code null} if none) + * and the exception (or {@code null} if none) of given stage as arguments. + *

+ * Unlike method {@link CompletionStage#handle handle} and like method + * {@link CompletionStage#whenComplete(BiConsumer) whenComplete}, + * this method is not designed to translate completion outcomes; + * whether the supplied action should not throw an exception or not, + * do NOT affect the given stage. + * + * @param action the action to perform + * @return the given stage + * @see CompletionStage#whenCompleteAsync(BiConsumer, Executor) + * @see java.util.stream.Stream#peek(Consumer) + */ + public static > C peekAsync( + C cf, BiConsumer action, Executor executor) { + cf.whenCompleteAsync(action, executor); + return cf; + } + //////////////////////////////////////////////////////////////////////////////// //# Backport CF static methods // compatibility for low Java versions diff --git a/cffu-core/src/main/java/io/foldright/cffu/package-info.java b/cffu-core/src/main/java/io/foldright/cffu/package-info.java index e0faea93..4a81deff 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/package-info.java +++ b/cffu-core/src/main/java/io/foldright/cffu/package-info.java @@ -3,7 +3,7 @@ *

* The core class is {@link io.foldright.cffu.Cffu}. And utils for {@link java.util.concurrent.CompletableFuture}, * the key user class is {@link io.foldright.cffu.CompletableFutureUtils} - * which contains the enhanced methods for {@link java.util.concurrent.CompletableFuture}. + * which contains the enhanced and backport methods for {@link java.util.concurrent.CompletableFuture}. * * @see io.foldright.cffu.Cffu * @see io.foldright.cffu.CffuFactory diff --git a/cffu-core/src/test/java/io/foldright/cffu/CffuTest.java b/cffu-core/src/test/java/io/foldright/cffu/CffuTest.java index 9d6e6589..8d7c3127 100644 --- a/cffu-core/src/test/java/io/foldright/cffu/CffuTest.java +++ b/cffu-core/src/test/java/io/foldright/cffu/CffuTest.java @@ -195,6 +195,31 @@ void test_cffuState() { assertEquals(CffuState.CANCELLED, incomplete.cffuState()); } + @Test + void test_peek() throws Exception { + BiConsumer c = (v, ex) -> { + }; + BiConsumer ec = (v, ex) -> { + throw anotherRte; + }; + + Cffu failed = cffuFactory.failedFuture(rte); + assertSame(failed.peek(c), failed); + assertSame(failed.peekAsync(c), failed); + assertSame(failed.peekAsync(c, executorService), failed); + assertSame(failed.peek(ec), failed); + assertSame(failed.peekAsync(ec), failed); + assertSame(failed.peekAsync(ec, executorService), failed); + + Cffu success = cffuFactory.completedFuture(n); + assertEquals(n, success.peek(c).get()); + assertEquals(n, success.peekAsync(c).get()); + assertEquals(n, success.peekAsync(c).get()); + assertEquals(n, success.peek(ec).get()); + assertEquals(n, success.peekAsync(ec).get()); + assertEquals(n, success.peekAsync(ec).get()); + } + //////////////////////////////////////////////////////////////////////////////// //# Cffu Re-Config methods // diff --git a/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java b/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java index 0cb9bf13..e6866e5c 100644 --- a/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java +++ b/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java @@ -788,6 +788,35 @@ void test_either_success() throws Exception { assertEquals(n, applyToEitherSuccessAsync(failed, cf, Function.identity(), executorService).get()); } + //////////////////////////////////////////////////////////////////////////////// + //# More new enhanced methods + //////////////////////////////////////////////////////////////////////////////// + + @Test + void test_peek() throws Exception { + BiConsumer c = (v, ex) -> { + }; + BiConsumer ec = (v, ex) -> { + throw anotherRte; + }; + + CompletableFuture failed = CompletableFuture.failedFuture(rte); + assertSame(peek(failed, c), failed); + assertSame(peekAsync(failed, c), failed); + assertSame(peekAsync(failed, c, executorService), failed); + assertSame(peek(failed, ec), failed); + assertSame(peekAsync(failed, ec), failed); + assertSame(peekAsync(failed, ec, executorService), failed); + + CompletableFuture success = completedFuture(n); + assertEquals(n, peek(success, c).get()); + assertEquals(n, peekAsync(success, c).get()); + assertEquals(n, peekAsync(success, c).get()); + assertEquals(n, peek(success, ec).get()); + assertEquals(n, peekAsync(success, ec).get()); + assertEquals(n, peekAsync(success, ec).get()); + } + //////////////////////////////////////////////////////////////////////////////// //# Backport CF methods // compatibility for low Java version diff --git a/cffu-kotlin/src/main/java/io/foldright/cffu/kotlin/CompletableFutureExtensions.kt b/cffu-kotlin/src/main/java/io/foldright/cffu/kotlin/CompletableFutureExtensions.kt index 9113db16..c423175b 100644 --- a/cffu-kotlin/src/main/java/io/foldright/cffu/kotlin/CompletableFutureExtensions.kt +++ b/cffu-kotlin/src/main/java/io/foldright/cffu/kotlin/CompletableFutureExtensions.kt @@ -727,6 +727,66 @@ fun CompletionStage.applyToEitherSuccessAsync( ): CompletableFuture = CompletableFutureUtils.applyToEitherSuccessAsync(this, cf2, fn, executor) +//////////////////////////////////////////////////////////////////////////////// +//# More new enhanced methods +//////////////////////////////////////////////////////////////////////////////// + +/** + * Peeks the result by executing the given action when this stage completes, returns this stage. + * + * When this stage is complete, the given action is invoked with the result (or `null` if none) + * and the exception (or `null` if none) of this stage as arguments. + * + * Unlike method [handle][CompletionStage.handle] and like method [whenComplete][CompletionStage.whenComplete], + * this method is not designed to translate completion outcomes; + * whether the supplied action should not throw an exception or not, do **NOT** affect this stage. + * + * @param action the action to perform + * @return this stage + * @see CompletionStage.whenComplete + * @see java.util.stream.Stream.peek + */ +fun > C.peek(action: BiConsumer): C = + CompletableFutureUtils.peek(this, action) + +/** + * Peeks the result by executing the given action when this stage completes, + * executes the given action using this stage's default asynchronous execution facility, returns this stage. + * + * When this stage is complete, the given action is invoked with the result (or `null` if none) + * and the exception (or `null` if none) of this stage as arguments. + * + * Unlike method [handle][CompletionStage.handle] and like method [whenComplete][CompletionStage.whenComplete], + * this method is not designed to translate completion outcomes; + * whether the supplied action should not throw an exception or not, do **NOT** affect this stage. + * + * @param action the action to perform + * @return this stage + * @see CompletionStage.whenCompleteAsync + * @see java.util.stream.Stream.peek + */ +fun > C.peekAsync(action: BiConsumer): C = + CompletableFutureUtils.peekAsync(this, action) + +/** + * Peeks the result by executing the given action when this stage completes, + * executes the given action using the supplied Executor, returns this stage. + * + * When this stage is complete, the given action is invoked with the result (or `null` if none) + * and the exception (or `null` if none) of this stage as arguments. + * + * Unlike method [handle][CompletionStage.handle] and like method [whenComplete][CompletionStage.whenComplete], + * this method is not designed to translate completion outcomes; + * whether the supplied action should not throw an exception or not, do **NOT** affect this stage. + * + * @param action the action to perform + * @return this stage + * @see CompletionStage.whenCompleteAsync + * @see java.util.stream.Stream.peek + */ +fun > C.peekAsync(action: BiConsumer, executor: Executor): C = + CompletableFutureUtils.peekAsync(this, action, executor) + //////////////////////////////////////////////////////////////////////////////// //# Backport CF instance methods // compatibility for low Java version