TS
JV

TypeScript to Java

10 lessons

Progress0%
1Introduction: Compiler to JVM2Type Systems: Structural vs Nominal3Classes & OOP4Generics5Modules to Packages6Null Safety7Async to Threads8Ecosystem9Exception Handling10Collections and Stream API
All Mirror Courses
TS
JV
Async to Threads
MirrorLesson 7 of 10
Lesson 7

Async to Threads

Async to Threads

Introduction

In this lesson, you'll learn about async to threads in Java. Coming from TypeScript, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
TS
From TypeScript:

In TypeScript, you're familiar with async to threads.

JV
In Java:

Java has its own approach to async to threads, which we'll explore step by step.

The Java Way

Let's see how Java handles this concept. Here's a typical example:

JV
Java Example
import java.util.concurrent.CompletableFuture;
import java.net.http.*;

CompletableFuture<String> fetchUser(int id) {
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("/api/users/" + id))
        .build();
    return client.sendAsync(request, BodyHandlers.ofString())
                 .thenApply(HttpResponse::body);
}

CompletableFuture<Void> loadAll() {
    CompletableFuture<String> u1 = fetchUser(1);
    CompletableFuture<String> u2 = fetchUser(2);
    return CompletableFuture.allOf(u1, u2);
}

Comparing to TypeScript

Here's how you might have written similar code in TypeScript:

TS
TypeScript (What you know)
async function fetchUser(id: number): Promise<User> {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

async function loadAll() {
  const [u1, u2] = await Promise.all([
    fetchUser(1),
    fetchUser(2),
  ]);
  return [u1, u2];
}
Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

JV
In Java:

TypeScript async/await is single-threaded event loop; Java is truly multi-threaded

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

JV
In Java:

CompletableFuture<T> is the Java equivalent of Promise<T>

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

JV
In Java:

CompletableFuture.allOf() is the Java equivalent of Promise.all()

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

JV
In Java:

Java 21 virtual threads (Project Loom) allow writing blocking code that scales like async

Step-by-Step Breakdown

1. CompletableFuture = Promise

CompletableFuture<T> represents a value that will be available in the future. thenApply() maps the result like .then() in JavaScript.

TS
TypeScript
fetchUser(1).then(user => user.name)
JV
Java
fetchUser(1).thenApply(user -> user.getName())

2. allOf = Promise.all

CompletableFuture.allOf() waits for all futures to complete. Unlike Promise.all(), it returns CompletableFuture<Void> — you need to call .get() on each future to retrieve results.

TS
TypeScript
const [a, b] = await Promise.all([fetchA(), fetchB()]);
JV
Java
CompletableFuture<String> fa = fetchA();
CompletableFuture<String> fb = fetchB();
CompletableFuture.allOf(fa, fb).join();
String a = fa.get();
String b = fb.get();

3. Java 21 Virtual Threads

Virtual threads (Java 21+) let you write blocking-style code that the JVM schedules efficiently — like async/await but without the colored function problem.

TS
TypeScript
async function handler(req: Request): Promise<Response> {
  const data = await db.query(req.id);
  return { data };
}
JV
Java
// With virtual threads — just write blocking code:
public Response handler(Request req) throws Exception {
    var data = db.query(req.id()); // blocks virtual thread, not OS thread
    return new Response(data);
}
Rule of Thumb
Prefer virtual threads for new Java 21+ services. Use CompletableFuture for complex async orchestration.

4. Exception Handling in Async Code

CompletableFuture wraps exceptions in CompletionException. Use .exceptionally() to handle errors, similar to .catch() in Promises.

TS
TypeScript
fetchUser(1).catch(err => console.error(err))
JV
Java
fetchUser(1).exceptionally(ex -> {
    System.err.println(ex.getMessage());
    return null;
});

Common Mistakes

When coming from TypeScript, developers often make these mistakes:

  • TypeScript async/await is single-threaded event loop; Java is truly multi-threaded
  • CompletableFuture<T> is the Java equivalent of Promise<T>
  • CompletableFuture.allOf() is the Java equivalent of Promise.all()
Common Pitfall
Don't assume Java works exactly like TypeScript. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • CompletableFuture<T> is the Java equivalent of Promise<T>
  • CompletableFuture.allOf() mirrors Promise.all()
  • Java is truly multi-threaded — async code can run in parallel, not just concurrently
  • Java 21 virtual threads allow blocking-style code that scales like async
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your TypeScript code in Java to practice these concepts.
PreviousNext