Skip to content

Commit

Permalink
Explain onComplete vs. onSuccess + onFailure
Browse files Browse the repository at this point in the history
Simplify the code by replacing onComplete + if-ar.succeeded-else with
either onComplete(res, e) or with other processing.

Avoiding onComplete + if-ar.succeeded-else results in more concise and understandable
code because it avoids the additional nesting of the if-else clause.

Extend the `exampleFuture*` code to discuss all possibilities how onComplete and
onSuccess/onFailure might be used.

Remove unused exampleFuture2 from lines 173-186. A new exampleFuture2 is created in line 145.
  • Loading branch information
julianladisch committed Jan 8, 2025
1 parent 56a122a commit e9fe56f
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 100 deletions.
21 changes: 21 additions & 0 deletions vertx-core/src/main/asciidoc/futures.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,27 @@ You cannot interact directly with the result of a future, instead you need to se
{@link examples.CoreExamples#exampleFuture1}
----

Instead of using `onComplete` with `if (ar.succeeded()) { … } else { … }` you can use the `onSuccess` and `onFailure` handlers:

[source,$lang]
----
{@link examples.CoreExamples#exampleFuture2}
----

Frequently the future variable can be avoided. Often the lambda parameter type is not needed by compiler and code readers, allowing to replace `(FileProps fileProps)` with `fileProps`. In a lambda code block with a single statement the semicolon and the curly braces can be removed. This yields a more concise code:

[source,$lang]
----
{@link examples.CoreExamples#exampleFuture3}
----

`.onSuccess` and `.onFailure` can be combined into the two parameter method `.onComplete`. This is slightly faster and takes slightly less memory, but might be less readable:

[source,$lang]
----
{@link examples.CoreExamples#exampleFuture4}
----

[CAUTION]
====
Do not confuse _futures_ with _promises_.
Expand Down
200 changes: 100 additions & 100 deletions vertx-core/src/main/java/examples/CoreExamples.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
* Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
Expand All @@ -22,14 +22,17 @@
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetServer;
import io.vertx.core.net.NetSocket;
import io.vertx.core.net.SocketAddress;
import io.vertx.core.spi.VertxMetricsFactory;
import io.vertx.core.spi.VertxTracerFactory;
import io.vertx.core.spi.cluster.ClusterManager;
import io.vertx.core.transport.Transport;

import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* Created by tim on 08/01/15.
Expand Down Expand Up @@ -73,19 +76,17 @@ public void example7(Vertx vertx) {
vertx.executeBlocking(() -> {
// Call some blocking API that takes a significant amount of time to return
return someAPI.blockingMethod("hello");
}).onComplete(res -> {
System.out.println("The result is: " + res.result());
});
})
.onSuccess(result -> System.out.println("The result is: " + result));
}

public void workerExecutor1(Vertx vertx) {
WorkerExecutor executor = vertx.createSharedWorkerExecutor("my-worker-pool");
executor.executeBlocking(() -> {
// Call some blocking API that takes a significant amount of time to return
return someAPI.blockingMethod("hello");
}).onComplete(res -> {
System.out.println("The result is: " + res.result());
});
})
.onSuccess(result -> System.out.println("The result is: " + result));
}

public void workerExecutor2(WorkerExecutor executor) {
Expand Down Expand Up @@ -127,7 +128,7 @@ public void clusteredVertxBuilder(VertxOptions options, ClusterManager clusterMa
.buildClustered();
}

public void exampleFuture1(Vertx vertx, Handler<HttpServerRequest> requestHandler) {
public void exampleFuture1(Vertx vertx) {
FileSystem fs = vertx.fileSystem();

Future<FileProps> future = fs.props("/my_file.txt");
Expand All @@ -142,6 +143,36 @@ public void exampleFuture1(Vertx vertx, Handler<HttpServerRequest> requestHandle
});
}

public void exampleFuture2(Vertx vertx) {
FileSystem fs = vertx.fileSystem();

Future<FileProps> future = fs.props("/my_file.txt");

future
.onSuccess((FileProps fileProps) -> {
System.out.println("File size = " + fileProps.size());
})
.onFailure((Throwable e) -> {
System.out.println("Failure: " + e.getMessage());
});
}

public void exampleFuture3(Vertx vertx) {
FileSystem fs = vertx.fileSystem();

fs.props("/my_file.txt")
.onSuccess(fileProps -> System.out.println("File size = " + fileProps.size()))
.onFailure(e -> System.out.println("Failure: " + e.getMessage()));
}

public void exampleFuture4(Vertx vertx) {
FileSystem fs = vertx.fileSystem();

fs.props("/my_file.txt")
.onComplete(fileProps -> System.out.println("File size = " + fileProps.size()),
e -> System.out.println("Failure: " + e.getMessage()));
}

public void promiseCallbackOrder(Future<Void> future) {
future.onComplete(ar -> {
// Do something
Expand Down Expand Up @@ -170,61 +201,56 @@ public void exampleFutureComposition1(Vertx vertx) {
});
}

public void exampleFuture2(Vertx vertx, Handler<HttpServerRequest> requestHandler) {
FileSystem fs = vertx.fileSystem();
public void exampleFutureComposition2(Vertx vertx) {

Future<FileProps> future = fs.props("/my_file.txt");
FileSystem fs = vertx.fileSystem();

future.onComplete((AsyncResult<FileProps> ar) -> {
if (ar.succeeded()) {
FileProps props = ar.result();
System.out.println("File size = " + props.size());
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
});
Future<Void> future = fs
.createFile("/foo")
// When the file is created (fut1), execute this:
.compose(v -> fs.writeFile("/foo", Buffer.buffer()))
// When the file is written (fut2), execute this:
.compose(v -> fs.move("/foo", "/bar"));
}

public void exampleFutureAll1(HttpServer httpServer, NetServer netServer) {
public Future<?> exampleFutureAll1(HttpServer httpServer, NetServer netServer) {
Future<HttpServer> httpServerFuture = httpServer.listen();

Future<NetServer> netServerFuture = netServer.listen();

Future.all(httpServerFuture, netServerFuture).onComplete(ar -> {
if (ar.succeeded()) {
// All servers started
} else {
return Future.all(httpServerFuture, netServerFuture)
.onFailure(e -> {
// At least one server failed
}
});
});
}

public void exampleFutureAll2(Future<?> future1, Future<?> future2, Future<?> future3) {
Future.all(Arrays.asList(future1, future2, future3));
}

public void exampleFutureAny1(Future<String> future1, Future<String> future2) {
Future.any(future1, future2).onComplete(ar -> {
if (ar.succeeded()) {
// At least one is succeeded
} else {
public Future<String> exampleFutureAny1(Future<String> future1, Future<String> future2) {
return Future.any(future1, future2)
.map(result -> // At least one is succeeded
future1.succeeded() ? future1.result() : future2.result())
.onFailure(e -> {
// All failed
}
});
});
}

public void exampleFutureAny2(Future<?> f1, Future<?> f2, Future<?> f3) {
Future.any(Arrays.asList(f1, f2, f3));
}

public void exampleFutureJoin1(Future<?> future1, Future<?> future2, Future<?> future3) {
Future.join(future1, future2, future3).onComplete(ar -> {
if (ar.succeeded()) {
// All succeeded
} else {
// All completed and at least one failed
}
});
public Future<String> exampleFutureJoin1(Future<String> future1, Future<String> future2, Future<String> future3) {
CompositeFuture compositeFuture = Future.join(future1, future2, future3);

return compositeFuture
.map(x -> {
// All completed, each is either succeeded or failed
return compositeFuture.<String>list().stream()
.filter(Objects::nonNull) // failed have null
.collect(Collectors.joining(", "));
});
}

public void exampleFutureJoin2(Future<?> future1, Future<?> future2, Future<?> future3) {
Expand Down Expand Up @@ -294,6 +320,16 @@ public void start(Promise<Void> startPromise) throws Exception {
}
});

// Or
future
.onSuccess(x -> startPromise.complete())
.onFailure(startPromise::fail);

// Or
future
.onComplete(x -> startPromise.complete(),
startPromise::fail);

// Or
future
.<Void>mapEmpty()
Expand Down Expand Up @@ -334,28 +370,14 @@ public void example9(Vertx vertx) {

}

public void example10(Vertx vertx) {
vertx
public Future<String> example10(Vertx vertx) {
return vertx
.deployVerticle(new MyOrderProcessorVerticle())
.onComplete(res -> {
if (res.succeeded()) {
System.out.println("Deployment id is: " + res.result());
} else {
System.out.println("Deployment failed!");
}
});
.onSuccess(deploymentID -> System.out.println("Deployment id is: " + deploymentID));
}

public void example11(Vertx vertx, String deploymentID) {
vertx
.undeploy(deploymentID)
.onComplete(res -> {
if (res.succeeded()) {
System.out.println("Undeployed ok");
} else {
System.out.println("Undeploy failed!");
}
});
vertx.undeploy(deploymentID);
}

public void example12(Vertx vertx) {
Expand Down Expand Up @@ -516,83 +538,61 @@ public void configureBSDOptions(Vertx vertx, boolean reusePort) {
vertx.createHttpServer(new HttpServerOptions().setReusePort(reusePort));
}

public void tcpServerWithDomainSockets(Vertx vertx) {
public Future<NetServer> tcpServerWithDomainSockets(Vertx vertx) {
NetServer netServer = vertx.createNetServer();

// Only available when running on JDK16+, or using a native transport
SocketAddress address = SocketAddress.domainSocketAddress("/var/tmp/myservice.sock");

netServer
return netServer
.connectHandler(so -> {
// Handle application
// Handle application
})
.listen(address)
.onComplete(ar -> {
if (ar.succeeded()) {
// Bound to socket
} else {
// Handle failure
}
});
.listen(address);
}

public void httpServerWithDomainSockets(Vertx vertx) {
public Future<HttpServer> httpServerWithDomainSockets(Vertx vertx) {
HttpServer httpServer = vertx.createHttpServer();

// Only available when running on JDK16+, or using a native transport
SocketAddress address = SocketAddress.domainSocketAddress("/var/tmp/myservice.sock");

httpServer
return httpServer
.requestHandler(req -> {
// Handle application
})
.listen(address)
.onComplete(ar -> {
if (ar.succeeded()) {
// Bound to socket
} else {
// Handle failure
}
});
.listen(address);
}

public void tcpClientWithDomainSockets(Vertx vertx) {
public Future<NetSocket> tcpClientWithDomainSockets(Vertx vertx) {
NetClient netClient = vertx.createNetClient();

// Only available when running on JDK16+, or using a native transport
SocketAddress addr = SocketAddress.domainSocketAddress("/var/tmp/myservice.sock");

// Connect to the server
netClient
.connect(addr)
.onComplete(ar -> {
if (ar.succeeded()) {
// Connected
} else {
// Handle failure
}
});
return netClient
.connect(addr);
}

public void httpClientWithDomainSockets(Vertx vertx) {
private Future<Void> process(Buffer buffer, String parameter) {
return Future.succeededFuture();
}

public Future<Void> httpClientWithDomainSockets(Vertx vertx) {
HttpClient httpClient = vertx.createHttpClient();

// Only available when running on JDK16+, or using a native transport
SocketAddress addr = SocketAddress.domainSocketAddress("/var/tmp/myservice.sock");

// Send request to the server
httpClient.request(new RequestOptions()
return httpClient.request(new RequestOptions()
.setServer(addr)
.setHost("localhost")
.setPort(8080)
.setURI("/"))
.compose(request -> request.send().compose(HttpClientResponse::body))
.onComplete(ar -> {
if (ar.succeeded()) {
// Process response
} else {
// Handle failure
}
});
.compose(request -> request.send())
.compose(HttpClientResponse::body)
.compose(buffer -> process(buffer, "some parameter")); // Process response
}
}

0 comments on commit e9fe56f

Please sign in to comment.