Как стать автором
Обновить

Комментарии 8

Большое спасибо, монументальная статья!

Огромное спасибо, статья космос

Много написано про подкапотную часть корутин, но эта статья наконец раскладывает нутнянку по полкам. Спасибо, добавил в сохраненные.

Поправьте fetchProfileInfo и fetchProfileData

Поправил, спасибо!

Отличная статья, огромный объем работ проделан.
Но возник вопрос по этому примеру и пришлось смотреть сорсы.

/*
Dispatchers.Main всегда переключает выполнение корутины 
на главный поток через Handler.post() механизм, 
даже если корутина и так выполняется на главном
*/
val uiScope = CoroutineScope(Dispatchers.Main + Job())
uiScope.launch {
    launch {
        println("I'm child coroutine #1")
    }
    launch {
        println("I'm child coroutine #2")
    }
    println("I'm parent coroutine")
}


В этом примере возникает вопрос, а как в итоге завершится parent coroutine, если во время ее resumeWith дочерние корутины еще не завершены и она завершиться не может.

Как и кто в итоге вызовет ее resumeWith, учитывая что вы упомянули что дочерние корутины не знают про ее continuation?

  1. Объект Continuation родительской корутины никак не связан с Continuation объектами дочерних корутин, поэтому когда завершатся последние результат не будет проброшен обратно в UiScopeParentBlock, да и в этом особо нет смысла, как например с withContext(), который гарантирует последовательный порядок выполнения с возвращением результата.


Ответом будет подписка на Job дочерних корутин, которая как раз и вызовет метод финализации стейта нашей корутины при последнем onCompletion.
При этом resumeWith не будет вызван повторно, но стейт корутины корректно финализируется

    private tailrec fun tryWaitForChild(state: Finishing, child: ChildHandleNode, proposedUpdate: Any?): Boolean {
        val handle = child.childJob.invokeOnCompletion(
            invokeImmediately = false,
            handler = ChildCompletion(this, state, child, proposedUpdate)
        )
        if (handle !== NonDisposableHandle) return true // child is not complete and we've started waiting for it
        val nextChild = child.nextChild() ?: return false
        return tryWaitForChild(state, nextChild, proposedUpdate)
    }

Возможно, полезно будет добавить или упомянуть этот момент

Вот здесь ошибка:

// (5)
fun resumeWith(result: Result) {
try {
val newResult = invokeSuspend(result)
if (newResult === COROUTINE_SUSPENDED) return
Result.success(newResult)
} catch (exception: Throwable) {
Result.failure(exception)
}
completion.resumeWith(newResult)
}

  1. Какой result всё-таки возвращает invokeSuspend (варианты помимо COROUTINE_SUSPENDED). Если это будет оно, то когда вызовется resumeWith?

  2. Result.success и Result.failure надо присвоить в новую переменную для completion.resumeWith, newResult вообще не того типа и под try {}

  3. Всё-таки я не понял, чем является completion, переданный в конструктор этого сгенерированного класса.

Огромное спасибо!

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации