Is it possible to turn a suspend function into a d...
# coroutines
u
Is it possible to turn a suspend function into a debounced one? i.e. to not use a flow but something like this
Copy code
fun msisdnType(msisdn: String) { 
   setState { copy(msisdnValid = PENDING)} 
   scope.launch { 
      val isValid = validator.validate(msisdn) <~~~~~~~~~~~~~
      setState { copy(msisdnValid = if (isValid) VALID else INVALID)} 
   } 
}
i.e. I want the validation to ne debounced yes im aware it should probably be a flow, just curious as how would one do that
e
so what is supposed to happen if the function is called more often than that
u
no-op
I guess it doesnt make sense..it would mean the function would have to return "unexpectedly", right? breaking the continuation contract I suppose
c
Suspend functions don’t work like the by themselves, they aren’t aware of how many times a function is called or anything like that (just like regular code isn’t). If you need that kind of behavior, you will need to change the logic a bit. Debouncing requests like this can be done fairly easily be passing the requests through a
Channel
and consuming them as a Flow, so you can process the requests one-by-one and apply the logic that way. Passing the requests through a Channel gives you a lot of flexibility in dealing with this kind of logic properly.
Copy code
private val validationChannel = Channel<String>()
private fun processValidation() {
    validationChannel
        .consumeAsFlow()
        // I think this is the right operator for what you want, but you may need to play around with it to get it working
        .buffer(capacity = Channel.RENDEZVOUS, onBufferOverflow = BufferOverflow.DROP_OLDEST)
        .map {
            val isValid = validator.validate(it)
            setState { copy(msisdnValid = if (isValid) VALID else INVALID) }
        }
        .launchIn(scope)
}

public fun msisdnType(msisdn: String) {
    setState { copy(msisdnValid = PENDING) }
    validationChannel.trySend(msisdn)
}
Or alternatively, in this case you can hold onto the
Job
returned by
scope.launch
and check to see if it’s still active, and skipping the launch if so.
Copy code
private var job: Job? = null
public fun msisdnType(msisdn: String) {
    setState { copy(msisdnValid = PENDING) }

    if(job?.isActive == true) {
        // there's a job already running, ignore the request
    } else {
        job = scope.launch {
            val isValid = validator.validate(msisdn)
            setState { copy(msisdnValid = if (isValid) VALID else INVALID)}
        }
    }
}
Note that the first snippet is thread-safe, since you’re just sending values to a Channel, which itself is thread-safe. The second snippet isn’t thread-safe, and if
msisdnType()
is called from multiple threads, there may be race conditions with settings/checking the job.