Async-Await support for Vertx using Project Loom.
Please note that this project is deprecated in favor of https://github.com/vert-x3/vertx-virtual-threads-incubator
import static com.augustnagro.vertx.loom.AsyncAwait.async;
import static com.augustnagro.vertx.loom.AsyncAwait.await;
Future<byte[]> buildPdf() {
return async(() -> {
List<Long> userIds = await(userIdsFromDb());
List<String> userNames = new ArrayList<>(userIds.size());
for (Long id : userIds) {
userNames.add(await(userNameFromSomeApi(id)))
}
byte[] pdf = await(somePdfBuilder(userIds))
System.out.println(userIds);
return pdf;
});
}
vs.
Future<byte[]> buildPdf() {
return userIdsFromDb().flatMap(userIds -> {
Future<List<String>> userNamesFuture =
Future.succeededFuture(new ArrayList<>());
for (Long userId : userIds) {
userNamesFuture = userNamesFuture.flatMap(list -> {
return userNameFromSomeApi(userId)
.map(userName -> {
list.add(userName);
return list;
});
});
}
return userNamesFuture.flatMap(userNames -> {
return buildPdf(userNames)
.onComplete(__ ->
System.out.println("Generated pdf for user ids: " + userIds)
);
});
});
<dependency>
<groupId>com.augustnagro</groupId>
<artifactId>vertx-loom</artifactId>
<version>0.2.2</version>
</dependency>
This library requires a JDK 18 Loom Preview Build and depends on vertx-core
v. 4.2.4.
JDK 18 is used instead of the new 19 previews because no IDEs work with 19 yet.
WARNING: this library uses class Continuation, which has recently been made private in the Loom OpenJDK fork. It is likely that Continuation
will return later in some form, either with a restricted API or virtual thread schedulers. In newer preview builds, we will use --add-exports
to get access.
async(Callable<A>)
returns Future<A>
. Within the provided Callable, you can await
Futures and program in an imperative style. Stack traces are also significantly improved in the case of errors.
The execution context remains on the current thread; no new threads, virtual or otherwise, are created by calling async
.
Finally, async
and await
calls can be nested to any depth although recursion is not stack-stafe.
Vertx is great as-is.
- It's Wicked Fast
- Has great docs
- There's a huge ecosystem of libraries, from database connectors to network proxies
- Actor-like Verticles provide threading guarantees that make it safe to use Loom's Continuation class directly
But there are some downsides too.
- Using Futures is harder to read & maintain than simple blocking code.
- It's hard to debug big Future chains in IDEs
- Stack traces are poor, especially if the Exception is thrown on a different thread than your current Vertx Context. For example, when
pgClient.prepareQuery("SELEC * FROM my_table").execute()
fails, any logged stacktrace won't show you where this code is. This is because the postgres client maintains its own pool of threads.
Project Loom solves all three issues. The goal of this project is to combine the performance of Vertx's event loop with the productivity of Loom's synchronous programming model.
- the
io.vertx.ext.unit.junit.RunTestOnContext
JUnit 4 rule is not working with this. See the simple implementation ofasyncTest
in this project.