Kotlin Coroutines - Detailed Animation Visualizer

Watch step-by-step execution with detailed logging

Cold Flow

Idle

Each collector triggers a new, independent flow execution from the beginning. Think of it like a YouTube video - each viewer starts from the beginning.

โ„๏ธ COLD FLOW = Fresh Execution Per Collector
๐ŸŽฏ When to use: When each collector should trigger its own fresh execution
  • Database queries - each UI component gets fresh data
  • Network requests - each subscriber triggers its own API call
  • File reading operations - each collector reads from start
  • User-specific calculations - personalized for each observer
val coldFlow = flow { // This block runs for EACH collector emit(1) delay(1000) emit(2) delay(1000) emit(3) } // Collector 1 starts coldFlow.collect { println(it) } // Collector 2 starts (independent execution) coldFlow.collect { println(it) }
Flow Execution Timeline

SharedFlow (Hot)

Idle

Broadcasts values to all active collectors. New collectors can get recent values from replay buffer. Like a live TV broadcast - you join wherever it currently is.

๐Ÿ”ฅ HOT FLOW = One Source, Many Receivers
๐ŸŽฏ When to use: When you want to broadcast events/updates to multiple listeners
  • Event bus - app-wide events (user login, logout, etc.)
  • Live data streams - stock prices, sensor readings
  • Chat messages - broadcast to all connected users
  • Progress updates - file upload/download progress to multiple UI components
val sharedFlow = MutableSharedFlow<Int>( replay = 2 // Keep last 2 values ) // Producer emits continuously launch { repeat(5) { sharedFlow.emit(it) delay(1000) } } // Collectors join at different times launch { sharedFlow.collect { } } // Gets all delay(2500) launch { sharedFlow.collect { } } // Gets replay + new
Broadcasting Timeline
๐Ÿ“ก SharedFlow broadcasts to all active subscribers
Replay Buffer:
-
-

StateFlow

Idle

Always holds one current value. New collectors immediately get the current state. Automatically skips consecutive duplicate values. Perfect for UI state.

๐Ÿ”„ Real-World Use Case: Best for representing current state that multiple components need:
  • UI state - current screen, theme mode, user preferences
  • User authentication state - logged in/out status
  • App configuration - settings that components need to read
  • Selected items - current selection in lists or navigation
val stateFlow = MutableStateFlow(0) // Update state stateFlow.value = 1 stateFlow.value = 2 stateFlow.value = 2 // Skipped! (duplicate) stateFlow.value = 3 // New collector gets current value (3) immediately stateFlow.collect { state -> println("State: $state") }
State Updates
Current State: 0
๐Ÿ’ก StateFlow always has a value and skips duplicate consecutive emissions

Channel

Idle

Hot stream for coroutine communication. Each value is consumed by only one receiver. Like a queue where items are removed when received.

๐Ÿ“ฌ CHANNEL = Queue-like Communication
๐Ÿ”„ Channel Types:
โ€ข Rendezvous (capacity = 0): Direct handoff, sender waits for receiver
โ€ข Buffered (capacity > 0): Queue with buffer space
  • Task queues - processing background jobs one at a time
  • Actor model - sending messages to actors for processing
  • Rate limiting - controlling flow of requests or events
  • Load balancing - distributing work among multiple workers
// Rendezvous Channel (default) - Direct handoff val rendezvous = Channel<Int>() // Sender waits until receiver is ready // Buffered Channel - Has queue space val buffered = Channel<Int>(capacity = 3) // Sender doesn't wait if buffer has space // Producer-Consumer pattern launch { channel.send(value) } launch { val received = channel.receive() }
Channel Types Demonstration
๐Ÿ“ฌ Watch the difference between Rendezvous and Buffered channels
Mode: Rendezvous (capacity = 0)
Producer
Channel:
Consumer

withContext

Idle

Switches the coroutine to a different dispatcher/thread and returns result. Essential for performing UI updates on Main thread or IO operations on IO thread.

๐Ÿ”„ Real-World Use Case: Essential for thread management in real apps:
  • Network calls on IO thread, then update UI on Main thread
  • Heavy computations on Default thread, preserve UI responsiveness
  • File operations on IO, then process results on CPU threads
  • Database queries on IO, then update UI components safely
suspend fun loadData() = coroutineScope { // Start on Main thread println("Starting on: ${Thread.currentThread()}") val data = withContext(Dispatchers.IO) { // Switch to IO thread readFromNetwork() } withContext(Dispatchers.Main) { // Back to Main thread updateUI(data) } }
Thread Context Switching
Main Thread
UI Operations
IO Thread
Network/File IO
Default Thread
CPU Operations

launch { }

Idle

Fire-and-forget coroutine. Starts a new coroutine and doesn't return a result. Returns a Job that can be used to cancel the coroutine.

๐Ÿš€ Real-World Use Case: Perfect for independent background tasks:
  • Logging events - fire-and-forget logging operations
  • Analytics tracking - send events without waiting for response
  • Cache warming - preload data in background
  • Background sync - periodic data synchronization
val job = launch { // This runs asynchronously println("Coroutine started") delay(2000) println("Coroutine finished") } // Main code continues immediately println("Main continues") // Can cancel if needed job.cancel()
Coroutine Execution

async { }

Idle

Creates a coroutine that returns a result. Returns Deferred<T> immediately. Use await() to get the actual result when needed.

โšก Real-World Use Case: Ideal when you need the result of async work:
  • Parallel API calls - fetch multiple data sources simultaneously
  • Concurrent computations - process multiple calculations at once
  • Parallel file processing - read/parse multiple files concurrently
  • Combined operations - wait for multiple async tasks to complete
val deferred = async { // This runs asynchronously delay(2000) return@async "Result!" } // Do other work... println("Doing other work") // Get result when needed val result = deferred.await() println("Got: $result")
Async Execution
Deferred
Created immediately
โ†’
Awaiting...
Result after await()

runBlocking

Idle

Blocks the current thread until the coroutine completes. Used in main() functions and tests. Avoid in production code!

โš ๏ธ Real-World Use Case: Limited use cases - be careful!:
  • Main function - bridging between blocking and suspending worlds
  • Unit tests - testing suspend functions in blocking test frameworks
  • Integration tests - waiting for async operations to complete
  • Script/utility functions - simple command-line tools
โŒ Avoid in Android UI code - blocks the Main thread!
fun main() = runBlocking { // This blocks the main thread println("Start") delay(2000) println("End") } // Nothing runs until runBlocking completes!
Thread Blocking
Main Thread

๐Ÿ“‹ Execution Log

Click any animation to see detailed logs here...
โ˜• Support this project
Buy Me a Coffee at ko-fi.com