簡體   English   中英

如何定義兩個協程之間的依賴關系?

[英]How to define a dependency between two coroutines?

考慮以下代碼:

init {
     // coroutine 1
     // this is and needs to be in a separate coroutine as the collection runs indefinite
    viewModelScope.launch {
        myService.someSharedFlow.collect {
            // handle values
        }
    }

    // coroutine 2
    viewModelScope.launch {
        // this shall not be executed before the subscription to the SharedFlow in coroutine 1 is set up
        // to make sure I don't miss any emitted values
        withContext(Dispatchers.IO) {
            myService.initialize() // will send a value through the flow after initialization
        }
    }
}

如何讓協程 2 等到協程 1 中的 SharedFlow 訂閱設置完成?

如果您想在向其中推送值之前等待收集器訂閱您的流程,我看到兩種解決方案:

  1. 如果您有MutableSharedFlow ,則可以使用subscriptionCount屬性來了解您的流程是否已被訂閱。
  2. 正如@broot 所說,也有可能在SharedFlow上使用SharedFlow 這不需要公開可變的 API。但是,如果可以有多個收集器,並且您希望確保只觸發一次初始化,則必須對其添加一些手動檢查。

這是一個示例程序,展示了如何使用這兩種解決方案:

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlin.time.Duration.Companion.seconds

fun main() : Unit = runBlocking {
    val flow = MutableSharedFlow<Int>()
    println(
        """
        Solution 1: subscriptionCount
        -----------------------------
        """.trimIndent())
    waitForSubsciptionCount(flow)

    println(
        """
        
        Solution 2: onSubscription
        -----------------------------
        """.trimIndent())
    reactToSubscription(flow)
}

suspend fun CoroutineScope.waitForSubsciptionCount(flow: MutableSharedFlow<Int>) {
    val collectJob = collectAfterDelay(flow)

    println("Waiting for subscription")
    flow.subscriptionCount.filter { it > 0 }.first()

    println("Subscription detected")
    flow.initialize()

    // Specific to this test program, to avoid it to hang indefinitely
    collectJob.cancel()
}

suspend fun CoroutineScope.reactToSubscription(flow: SharedFlow<Int>) {
    println("Waiting for subscription")

    // This is optional. Helps to detect "end" of the flow.
    val initialized = MutableStateFlow(false)

    val initializableFlow = flow.onSubscription {
        println("Subscription detected")
        initialize()
        initialized.emit(true)
    }

    val collectJob = collectAfterDelay(initializableFlow)

    // Specific to this test program, to avoid it to  indefinitely
    initialized.filter { it == true }.first()
    collectJob.cancel()
}

private fun CoroutineScope.collectAfterDelay(flow: Flow<Int>) = launch {
    delay(1.seconds)
    print("Start collecting")
    flow.collect { println("collected: $it") }
}

suspend fun FlowCollector<Int>.initialize() {
    for (i in 1..3) {
        emit(i)
        println("Emitted $i")
    }
}

output 是:

Solution 1: subscriptionCount
-----------------------------
Waiting for subscription
Start collectingSubscription detected
collected: 1
Emitted 1
collected: 2
Emitted 2
collected: 3
Emitted 3

Solution 2: onSubscription
-----------------------------
Waiting for subscription
Start collectingSubscription detected
collected: 1
Emitted 1
collected: 2
Emitted 2
collected: 3
Emitted 3

注意:上面的兩種解決方案還應該允許您在服務內部移動初始化觸發器,並對消費者完全隱藏它。 這樣,調用您的服務的代碼就不需要破壞您的服務初始化。

編輯:這是在訂閱者開始收集后觸發流量發射的服務示例:

class SubscriptionCountService {
    private val _flow = MutableSharedFlow<Int>()
    public  val  flow = _flow.asSharedFlow()

    init {
        _flow.subscriptionCount.filter { it > 0 }
                               .take(1)
                               .onCompletion {
                                   println("First subscription detected: initialize")
                                   _flow.initialize() 
                               }
                               .launchIn(CoroutineScope(Dispatchers.IO))
    }
}


fun main() : Unit = runBlocking {
    val flow = SubscriptionCountService().flow
    launch {
        delay(1.seconds)
        println("Start collecting")
        // Collect a limited number of entries to terminate main program easily
        flow.take(3)
            .collect { println("collected: $it") }
    }
}

output 是:

Start collecting
collected: 1
Emitted 1
collected: 2
Emitted 2
collected: 3
Emitted 3

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM