Created
July 3, 2025 08:37
-
-
Save gbajaj/1ab99cdb67f97ae7c21409fdd31cc713 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import kotlinx.coroutines.* | |
import kotlinx.coroutines.flow.* | |
/** | |
* A ViewModel to manage the logic of a single item in our shopping cart. | |
*/ | |
class ShoppingCartItemViewModel { | |
// A private MutableStateFlow to hold the current quantity of the item. | |
// This will be updated by the UI when the user clicks the '+' or '-' buttons. | |
private val _quantity = MutableStateFlow(1) | |
init { | |
// This is where the magic happens. We create a coroutine scope | |
// to collect the flow of quantity changes. | |
val scope = CoroutineScope(Dispatchers.Default) | |
scope.launch { | |
_quantity | |
// First, we skip the initial value of 1, as we don't need to validate it on startup. | |
.drop(1) | |
// THIS IS THE SOLUTION: We apply the debounce operator. | |
// It will wait for 300 milliseconds of silence before emitting the latest value. | |
// If a new value arrives within that 300ms window, the timer resets. | |
.debounce(300L) | |
.collect { qty -> | |
// This block will only execute after the user has stopped changing the quantity. | |
println("\n-----------------------------------------") | |
println("USER PAUSED. Final quantity is $qty.") | |
println("Updating cart...") | |
updateCart(qty) | |
println("-----------------------------------------\n") | |
} | |
} | |
} | |
/** | |
* Simulates the user updating the quantity. In a real app, this would be | |
* called by the onClick listeners of the '+' and '-' buttons. | |
*/ | |
fun updateQuantity(newQuantity: Int) { | |
if (newQuantity > 0) { | |
println("User set quantity to: $newQuantity") | |
_quantity.value = newQuantity | |
} | |
} | |
/** | |
* Simulates the expensive operations (network calls, calculations). | |
* We add delays to represent the time these operations would take. | |
*/ | |
private suspend fun updateCart(quantity: Int) { | |
// 1. Validate Inventory (simulated network call) | |
println("1. Checking inventory for $quantity items...") | |
delay(150) | |
println(" -> Inventory confirmed.") | |
// 2. Recalculate Totals | |
println("2. Recalculating totals...") | |
delay(50) | |
println(" -> Totals updated.") | |
// 3. Check for Promotions (simulated network call) | |
println("3. Checking for promotions...") | |
delay(200) | |
println(" -> No new promotions applied.") | |
println("Cart update complete!") | |
} | |
} | |
/** | |
* The main function to run our simulation. | |
*/ | |
fun main() = runBlocking { | |
val viewModel = ShoppingCartItemViewModel() | |
println("User is rapidly clicking the '+' button...") | |
// Simulate a user clicking '+' 5 times in quick succession. | |
// Notice how each quantity change is printed immediately, but the | |
// expensive `updateCart` function is not called for each one. | |
for (i in 2..6) { | |
viewModel.updateQuantity(i) | |
delay(150) // User clicks every 150ms | |
} | |
// Keep the main function alive long enough to see the debounced result. | |
delay(1000) | |
println("Simulation finished.") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment