Hylke Bron
01/26/2024, 10:40 AMCompletableJob
before continuing its work (updating a state).
Simultaneously there is another (suspending) function (triggered from the UI for example) which completes that CompletableJob once its called.
So far so good, but now I want this second function to only return once the first suspending function had a chance to do its work
I've written a simple test to demonstrate my problems, and my solution so far.
My solution now is to call yield()
in the second suspending function, to give the first suspending function back control to do its work.
This works in a simple case, but once both functions are included in more complex codebases i got to a situation where the first function was wrapped in another lauch method, and in that case just doing 1 yield()
is not enough, in that case I needed to do 2 yield()
calls.
I was wondering if there is another solution? Ideally without one or many yield calls.
I've attached the samples in the thread (4 unit tests)
Note that this is a very simplified example, in my real world example its not a double join, but a join in a function thats wrapped in an async statement which is wrapped in a launch statement divided over sevaral layers / classesHylke Bron
01/26/2024, 10:41 AMJoffrey
01/26/2024, 10:45 AMCompletableJob
for the other synchronization instead of setting a boolean?Joffrey
01/26/2024, 10:46 AMcoroutineScope { ... }
in the first coroutine, launch
already gives you a scope and already waits for childrenHylke Bron
01/26/2024, 10:49 AMHylke Bron
01/26/2024, 10:50 AMSam
01/26/2024, 11:05 AMcompletableJob
using invokeOnCompletion
. That way it will run synchronously during the call to complete()
, and when complete()
returns it is guaranteed to have finished.
I think that's the only way to get your current design to work. If you can't use that approach, you may need to redesign the code to include another way for the two jobs to communicate, like another Job
or a Channel
.Sam
01/26/2024, 11:07 AMlaunch
job to the completableJob
, but that gets a bit too messy for my liking.franztesca
01/26/2024, 11:09 AM// instead of
// completableJob.complete()
// yield()
// call directly
doStuff()
// or start on another scope
scope.launch {
doStuff()
}.join()
if you cannot model it like this and need a shared state, then use a StateFlow
val stateOfTheStuff = MutableStateFlow(NOT_INVOKED_YET)
launch {
stateOfTheStuff.first { it == INVOKED }
doStuff()
stateOfTheStuff.value = COMPLETED
}
launch {
stateOfTheStuff.value = INVOKED
stateOfTheStuff.first { it === COMPLETED }
// do the rest
}
Hylke Bron
01/26/2024, 11:13 AMfirst
statement.
Its for a state machine, and completing the job should trigger a state transition, but after completing the job, you still have to wait for the transition to be complete (so I added an awaitTransition()
method that I use right now):
suspend fun awaitTransition(state: T) {
stateFlow.filter { it != state }.first()
}
Hylke Bron
01/26/2024, 11:17 AMSam
01/26/2024, 11:37 AMHylke Bron
01/26/2024, 11:38 AMstreetsofboston
01/26/2024, 1:30 PM