Получение обновлений статуса от сопрограммы - PullRequest
0 голосов
/ 07 февраля 2019

Рассмотрим асинхронный API, который сообщает о ходе выполнения своих операций:

suspend fun operationWithIO(input: String, progressUpdate: (String) -> Unit): String {
    withContext(Dispatchers.IO) {
        // ...
    }
}

Возможно ли реализовать вызовы к progressUpdate таким образом, чтобы обратные вызовы обрабатывались диспетчером вызывающей стороны ?Или есть лучший способ доставить обновления статуса обратно вызывающей стороне?

Ответы [ 2 ]

0 голосов
/ 08 февраля 2019

Как насчет обертки функции обратного вызова и вызова обернутой функции:

/** Return a new callback that invokes this callback on the current context. */
suspend fun <T> ((T) -> Unit).onCurrentContext(): (T) -> Unit =
    coroutineContext.let { context ->
        { value: T ->
            runBlocking {
                launch(context) {
                    this@onCurrentContext.invoke(value)
                }
            }
        }
    }

/** Perform a background operation, delivering status updates on the caller's context. */
suspend fun operationWithIO(statusUpdate: (String) -> Unit): String {
    val cb = statusUpdate.onCurrentContext()
    return withContext(Dispatchers.IO) {
        cb("Phase 1")
        delay(150)
        cb("Phase 2")
        delay(150)
        "Result"
    }
}

// In use
runBlocking {
    val result = operationWithIO {
        println("received callback status $it")
    }
    println("result is $result")
}
0 голосов
/ 07 февраля 2019

Вы должны отправлять обновления о прогрессе на канале.Это позволит вызывающему абоненту прослушивать канал, используя тот диспетчер, который он хочет.

suspend fun operationWithIO(input: String, progressChannel: Channel<String>): String {
    withContext(Dispatchers.IO) {
        // ...

        progressChannel.send("Done!")
        progressChannel.close()
    }
}

Вызывающий может использовать его, выполнив что-то вроде этого:

val progressChannel = Channel<String>()

someScope.launch {
    operationWithIO(input, progressChannel)
}

// Remember the call to progressChannel.close(), so that this iteration stops.
for (progressUpdate in progressChannel) {
    println(progressUpdate)
}
...