본 내용은 Eliminating coroutine races 을 읽고 입맛대로 정리한 글입니다.
fun main() {
repeat(100) { GlobalScope.launch { Thread.sleep(100) } } // To make processor busy
test() // sometimes "ab" and sometimes "ba"
runBlocking { scope.coroutineContext.job.children.forEach { it.join() } }
}
fun test() {
a()
b()
}
val scope = CoroutineScope(Dispatchers.Default)
fun a() = scope.launch {
print("a")
}
fun b() = scope.launch {
print("b")
}
val scope = CoroutineScope(Dispatchers.Default)
val job: Job? = null
fun a() {
job = scope.launch {
print("a")
}
}
fun b() = scope.launch {
job?.join()
print("b")
}
join()
을 사용하여 첫 번째 프로세스가 완료될 때까지 기다림class BaseViewModel : ViewModel() {
private val eventFlow = MutableSharedFlow<Event>()
init {
eventFlow
// ...
.onEach(::handleIntent)
// ...
.launchIn(viewModelScope)
}
fun sendEvent(event: Event) {
viewModelScope.launch {
intentFlow.emit(event)
}
}
fun handleIntent(event: Event) {
// ...
}
}
class SomeViewModel : BaseViewModel() {
init {
sendEvent(Event()) // 처리될 것인가 말 것인가?
}
}
eventFlow
를 listen한 코루틴 시작Event()
를 보냄(sendEvent
)SharedFlow
는 기본적으로 replay가 0으로 설정되어 있기 때문class BaseViewModel : ViewModel() {
private val eventFlow = MutableSharedFlow<Event>()
private val eventFlowJob: Job // 👈
init {
eventFlowJob = eventFlow // 👈
// ...
.onEach(::handleIntent)
// ...
.launchIn(viewModelScope)
}
fun sendEvent(event: Event) {
viewModelScope.launch {
eventFlowJob.join() // 무한 대기!
intentFlow.emit(event)
}
}
fun handleIntent(event: Event) {
// ...
}
}
class SomeViewModel : BaseViewModel() {
init {
sendEvent(Event())
}
}
job
을 활용하여 개선할 수 없음class BaseViewModel : ViewModel() {
private val eventFlow =
MutableSharedFlow<Event>(replay = Int.MAX_VALUE) // 👈
init {
eventFlow
// ...
.onEach(::handleIntent)
// ...
.launchIn(viewModelScope)
}
fun sendEvent(event: Event) {
viewModelScope.launch {
intentFlow.emit(event)
}
}
fun handleIntent(event: Event) {
// ...
}
}
class SomeViewModel : BaseViewModel() {
init {
sendEvent(Event())
}
}
class BaseViewModel : ViewModel() {
private val eventFlow = MutableSharedFlow<Event>()
private val eventFlowListenerStarted = CompletableDeferred<Unit>() // 👈
init {
eventFlow
.onSubscription { eventFlowListenerStarted.complete(Unit) } // 👈
// ...
.onEach(::handleIntent)
// ...
.launchIn(viewModelScope)
}
fun sendEvent(event: Event) {
viewModelScope.launch {
eventFlowListenerStarted.join() // 👈
intentFlow.emit(event)
}
}
fun handleIntent(event: Event) {
// ...
}
}
class BaseViewModel : ViewModel() {
private val eventFlow = MutableSharedFlow<Event>()
private val eventFlowListenerStarted = Job() // 👈
init {
eventFlow
.onSubscription { eventFlowListenerStarted.complete() } // 👈
// ...
.onEach(::handleIntent)
// ...
.launchIn(viewModelScope)
}
fun sendEvent(event: Event) {
viewModelScope.launch {
eventFlowListenerStarted.join() // 👈
intentFlow.emit(event)
}
}
fun handleIntent(event: Event) {
// ...
}
}
join
을 사용하여 기다리고, complete
를 사용하여 완료할 수 있음onSubscription
operatoronStart
operator를 활용할 수도 있음class BaseViewModel : ViewModel() {
private val eventFlow = MutableSharedFlow<Event>()
init {
eventFlow
// ...
.onEach(::handleIntent)
// ...
.launchIn(viewModelScope)
}
fun sendEvent(event: Event) {
viewModelScope.launch {
waitForListener()
intentFlow.emit(event)
}
}
fun handleIntent(event: Event) {
// ...
}
suspend fun waitForListener() { // 👈
val subscriptionCount = eventFlow.subscriptionCount
if (subscriptionCount.value == 0) {
subscriptionCount
.filter { it > 0 }
.first()
}
}
}