Java async support: Native non-blocking SDKs with reactive streaming
Vishal Gowda
September 3, 2025 - 5 min read
Product Updates
Java SDKs, now async‑native: CompletableFuture + Reactive Streams
Java’s ecosystem has steadily shifted toward asynchronous, event‑driven programming. Reactive architectures are increasingly adopted by major platforms1, while CompletableFuture is ubiquitous in modern APIs. For key reactive concepts, see the Reactive Manifesto glossary . SDKs should match this async‑first reality—without forcing teams to abandon familiar synchronous code.
What’s new
Async native: CompletableFuture<T> returned by standard methods.
Streaming built‑in: Reactive Streams Publisher<T> for pagination, SSE, JSONL, and file I/O.
Dual SDK: Synchronous by default; opt in with .async() per call‑site or service.
Blob: A light, framework‑agnostic byte‑stream abstraction for efficient uploads/downloads.
Why async
Traditional blocking SDKs dedicate one thread per in‑flight request. That results in idle threads, wasted memory, and hard‑to‑tune pools. Especially under high concurrency and variable network latency. Async I/O scales with a small, fixed number of threads without sacrificing composition2 or backpressure 3.
Async I/O is already the standard everywhere else
Python has asyncio, JavaScript has async/await with Promise, Go has goroutines, and Swift has async/await. Async I/O is the de facto model across modern languages. Java’s ecosystem is rapidly moving in this direction—think of this as Python’s asyncio but for Java, with the added benefits of strong typing and mature reactive ecosystems.
When async provides the most value
Scenario
Compositional orchestration
Description
Workflows with multiple dependent or parallel API calls (microservices orchestration, data enrichment)
Async Benefit
Futures/streams model sequencing, fan‑out, retries, and timeouts declaratively instead of blocking threads
Latency tolerance
Description
Remote services with unpredictable or spiky response times
Async Benefit
Async frees threads during waits, so slow calls don't exhaust the pool or block unrelated work
Concurrency scaling
Description
Thousands of requests in parallel (web servers, batch processors)
Async Benefit
Decouples request count from thread count — small event pools service massive concurrency without blowing up memory
Event‑driven pipelines
Description
Real‑time filtering, transformation, and routing (SSE, JSONL streams, pagination)
Async Benefit
Lean on mature reactive ecosystems—Project Reactor, RxJava, Akka Streams—with proven operators for complex event processing
Resource efficiency
Description
Cloud or edge environments with strict memory/cost budgets
Async Benefit
Far fewer thread stacks are required — often 80–90% lower memory under load, enabling smaller, cheaper deployments
Scenario
Description
Async Benefit
Compositional orchestration
Workflows with multiple dependent or parallel API calls (microservices orchestration, data enrichment)
Futures/streams model sequencing, fan‑out, retries, and timeouts declaratively instead of blocking threads
Latency tolerance
Remote services with unpredictable or spiky response times
Async frees threads during waits, so slow calls don't exhaust the pool or block unrelated work
Concurrency scaling
Thousands of requests in parallel (web servers, batch processors)
Decouples request count from thread count — small event pools service massive concurrency without blowing up memory
Event‑driven pipelines
Real‑time filtering, transformation, and routing (SSE, JSONL streams, pagination)
Lean on mature reactive ecosystems—Project Reactor, RxJava, Akka Streams—with proven operators for complex event processing
Resource efficiency
Cloud or edge environments with strict memory/cost budgets
Far fewer thread stacks are required — often 80–90% lower memory under load, enabling smaller, cheaper deployments
SDK design insight for API producers
Your developers have diverse needs: some build high-concurrency microservices that benefit from async patterns, while others prefer straightforward synchronous flows. Don’t make them choose at the SDK level—cater to both paradigms in one unified SDK so they can adopt async where it adds value without abandoning familiar synchronous code. The most successful SDKs optimize for developer choice, not architectural dogma.
Here’s how this works in practice. We expose both sync and async interfaces from one SDK:
Backward compatibility
The builder yields a synchronous SDK by default, so no breaking changes. Opt into async via .async(); the mode applies consistently across sub‑SDKs.
Our SDKs go to lengths to ensure there is as little as possible thread blocking work. For standard JSON responses, we collect and decode bytes asynchronously using thenApply/thenCompose. For streams, we hook into Flow.Publisher<List<ByteBuffer>>.
Async iterables via Reactive Streams
For async iterables (pagination, streaming responses), we represent them as Reactive Streams [Publisher<T>](https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.4/README.md#1-publisher-code ). Traditional Java collections or custom iterators aren’t feasible for async scenarios—they can’t express non‑blocking backpressure or handle variable consumption rates gracefully. Using Publisher<T> is an increasingly common idiom in modern Java applications, providing seamless interoperability with mature reactive ecosystems like Project Reactor , RxJava , Akka Streams , Vert.x , and Quarkus Mutiny . We keep dependencies light by implementing JDK‑native operators (map, mapAsync, concat, wrap, flatten) through custom publishers, subscribers, and subscriptions.
Protocol‑aware streaming
SSE & JSONL: Custom publishers bridge raw byte streams to protocol parsers, yielding typed events on backpressure‑aware Publisher<T>
Pagination: A generic, non‑blocking pagination publisher drives page fetches using pluggable ProgressTrackerStrategy to parse cursors and termination conditions
Retries, timeouts, and hooks
Async retries use ScheduledExecutorService for non‑blocking exponential backoff with jitter
Timeouts/cancellation4 are surfaced with CompletableFuture#orTimeout, completeExceptionally, and cancellation propagation to HTTP requests
Hook transformations leverage CompletableFuture for zero‑blocking customization points
Efficient payloads with Blob
Blob is our core abstraction for working with streams of ByteBuffer:
Multipart uploads concatenate publishers—each part (form field or file) is its own stream. Downloads expose Blob so you can stream to disk without buffering whole payloads.
Streaming uploads for synchronous SDKs
Enable enableStreamingUploads: true to get the same memory-efficient upload capabilities
Reactive Streams vs JDK Flow
We expose Reactive Streams types in the public API for ergonomic use with the wider ecosystem. Java 11+ HTTP APIs expose Flow.Publisher under the hood; we convert via FlowAdapters and flatten List<ByteBuffer> as needed. If you require Flow.Publisher, adapters are available in both directions.
Reactive streaming patterns
Reactor
RxJava
Akka Streams
On Virtual Threads
Virtual threads make synchronous code scale far better by reducing the cost of blocking. They’re a great fit for simple request/response flows and can be used with the synchronous SDK today. Our async SDK still adds value where you need backpressure, streaming, cancellation, and composition across multiple concurrent I/O operations—patterns that CompletableFuture/Publisher express naturally.
For API Producers: the unified paradigm advantage
Broader reach: Works for Spring MVC (sync) and WebFlux/Quarkus (async)
Migration flexibility: Adopt async gradually without replacing integrations
Lower maintenance: One codebase; two clean interfaces
Consistent DX: Same auth, errors, and config everywhere
Getting started
Enable the following flag in your .speakeasy/gen.yaml:
Then run speakeasy run to regenerate your SDK.
For existing SDKs, see our migration guide for step-by-step instructions on enabling async support for your SDKs.
Looking ahead
This release lays the groundwork for the full spectrum of modern Java patterns—from cloud‑native microservices to real‑time streaming systems. Ready to unlock the benefits of non‑blocking I/O? Regenerate your Java SDK and try it today.
Composition — The ability to chain and combine async operations declaratively, like future.thenCompose() or stream operators, without manual thread coordination. ↩
Cancellation — The ability to interrupt or stop async operations gracefully, propagating cancellation signals through composed operations to free resources and avoid unnecessary work. ↩