Playground¶
Interactive examples powered by Kotlin Playground. Edit and run directly in your browser.
Parallel Execution — .with¶
Three services fetched in parallel. Total time = max(individual), not sum.
import kotlinx.coroutines.*
data class Dashboard(val user: String, val cart: String, val promos: String)
suspend fun fetchUser(): String { delay(300); return "Alice" }
suspend fun fetchCart(): String { delay(200); return "3 items" }
suspend fun fetchPromos(): String { delay(100); return "SAVE20" }
suspend fun main() {
val start = System.currentTimeMillis()
// Parallel execution with coroutineScope
val result = coroutineScope {
val dUser = async { fetchUser() }
val dCart = async { fetchCart() }
val dPromos = async { fetchPromos() }
Dashboard(dUser.await(), dCart.await(), dPromos.await())
}
val elapsed = System.currentTimeMillis() - start
println("Result: $result")
println("Time: ${elapsed}ms (not 600ms — parallel!)")
// With KAP this would be:
// val result = Async {
// kap(::Dashboard)
// .with { fetchUser() }
// .with { fetchCart() }
// .with { fetchPromos() }
// }
}
Note
KAP itself can't run in Kotlin Playground (it needs a Maven dependency). These examples show the coroutine patterns that KAP simplifies. The commented KAP code shows the equivalent.
Phase Barriers — .then¶
Phase 2 waits for phase 1 to complete:
import kotlinx.coroutines.*
suspend fun fetchA(): String { delay(200); return "A" }
suspend fun fetchB(): String { delay(150); return "B" }
suspend fun validate(a: String, b: String): String { delay(100); return "valid($a,$b)" }
suspend fun main() {
val start = System.currentTimeMillis()
val result = coroutineScope {
// Phase 1: parallel
val dA = async { fetchA() }
val dB = async { fetchB() }
val a = dA.await()
val b = dB.await()
// Phase 2: barrier — needs A and B
val validated = validate(a, b)
Triple(a, b, validated)
}
val elapsed = System.currentTimeMillis() - start
println("Result: $result")
println("Time: ${elapsed}ms (200ms parallel + 100ms barrier = ~300ms)")
// With KAP:
// kap(::Triple)
// .with { fetchA() } // ┐ parallel
// .with { fetchB() } // ┘
// .then { validate() } // ── barrier
}
Value-Dependent Phases — .andThen¶
Phase 2 uses phase 1's result:
import kotlinx.coroutines.*
data class UserContext(val profile: String, val prefs: String)
data class Enriched(val recs: String, val promos: String)
suspend fun fetchProfile(id: String): String { delay(200); return "profile-$id" }
suspend fun fetchPrefs(id: String): String { delay(150); return "prefs-$id" }
suspend fun fetchRecs(profile: String): String { delay(100); return "recs-for-$profile" }
suspend fun fetchPromos(prefs: String): String { delay(80); return "promos-for-$prefs" }
suspend fun main() {
val start = System.currentTimeMillis()
// Phase 1
val ctx = coroutineScope {
val dProfile = async { fetchProfile("user-42") }
val dPrefs = async { fetchPrefs("user-42") }
UserContext(dProfile.await(), dPrefs.await())
}
// Phase 2 — depends on ctx
val enriched = coroutineScope {
val dRecs = async { fetchRecs(ctx.profile) }
val dPromos = async { fetchPromos(ctx.prefs) }
Enriched(dRecs.await(), dPromos.await())
}
val elapsed = System.currentTimeMillis() - start
println("Context: $ctx")
println("Enriched: $enriched")
println("Time: ${elapsed}ms (200ms + 100ms = ~300ms, not 530ms)")
// With KAP — one flat chain:
// kap(::UserContext)
// .with { fetchProfile("user-42") }
// .with { fetchPrefs("user-42") }
// .andThen { ctx ->
// kap(::Enriched)
// .with { fetchRecs(ctx.profile) }
// .with { fetchPromos(ctx.prefs) }
// }
}
Partial Failure — .settled()¶
One service fails, the rest still complete:
import kotlinx.coroutines.*
data class Dashboard(val user: String, val cart: String, val config: String)
suspend fun fetchUserMayFail(): String { throw RuntimeException("user service down") }
suspend fun fetchCart(): String { delay(100); return "cart-ok" }
suspend fun fetchConfig(): String { delay(80); return "config-ok" }
suspend fun main() {
// With raw coroutines: supervisorScope + try/catch
val result = supervisorScope {
val dUser = async { fetchUserMayFail() }
val dCart = async { fetchCart() }
val dConfig = async { fetchConfig() }
val user = try { dUser.await() } catch (e: Exception) { "anonymous" }
Dashboard(user, dCart.await(), dConfig.await())
}
println("Result: $result")
println("User failed but dashboard still built with fallback!")
// With KAP:
// kap { user: Result, cart: String, config: String ->
// Dashboard(user.getOrDefault("anonymous"), cart, config)
// }
// .with(Kap { fetchUserMayFail() }.settled())
// .with { fetchCart() }
// .with { fetchConfig() }
}
Racing — First to Succeed Wins¶
import kotlinx.coroutines.*
import kotlinx.coroutines.selects.select
suspend fun fetchUS(): String { delay(300); return "US-data" }
suspend fun fetchEU(): String { delay(100); return "EU-data" }
suspend fun fetchAP(): String { delay(200); return "AP-data" }
suspend fun main() {
val start = System.currentTimeMillis()
// Race three regions
val winner = coroutineScope {
val us = async { fetchUS() }
val eu = async { fetchEU() }
val ap = async { fetchAP() }
select {
us.onAwait { it }
eu.onAwait { it }
ap.onAwait { it }
}.also {
us.cancel(); eu.cancel(); ap.cancel()
}
}
val elapsed = System.currentTimeMillis() - start
println("Winner: $winner in ${elapsed}ms")
println("EU won at ~100ms. US and AP cancelled.")
// With KAP:
// raceN(
// Kap { fetchUS() },
// Kap { fetchEU() },
// Kap { fetchAP() },
// )
}
Bounded Concurrency — traverse(concurrency)¶
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
suspend fun fetchUser(id: Int): String { delay(100); return "user-$id" }
suspend fun main() {
val ids = (1..20).toList()
val start = System.currentTimeMillis()
// Bounded parallel: max 5 concurrent
val semaphore = Semaphore(5)
val results = coroutineScope {
ids.map { id ->
async { semaphore.withPermit { fetchUser(id) } }
}.awaitAll()
}
val elapsed = System.currentTimeMillis() - start
println("Fetched ${results.size} users in ${elapsed}ms")
println("With concurrency=5: ~400ms (4 batches × 100ms)")
println("Sequential would be: ${ids.size * 100}ms")
// With KAP:
// ids.traverse(concurrency = 5) { id -> Kap { fetchUser(id) } }
}
Ready to try KAP? Get Started