Шпаргалка: как выглядит Kotlin Coroutine без макияжа

от автора

Пытаюсь лучше понять работу Kotlin Coroutine. Беру небольшую тему про Kotlin Coroutine и пытаюсь разобраться и написать шпаргалку (максимально кратко и лаконично). Сегодня сильно упрощенно о том, как выглядит скомпилированный класс корутины.

При создании и запуске корутины компилятор создаст специальный объект, который реализует интерфейс Continuation (другими словами, возможно будет понятнее: объект представляет собой реализацию лямбды, переданной в launch и у этого объекта будет своё соcтояние, которое будет меняться по мере выполнения кода, находящегося в этой лямбде). Этот класс содержит всю логику выполнения корутины, включая её состояние и локальные переменные.

Также будут созданы объекты для каждой suspend функции, но в этот раз их не рассматриваем.

Жизненный цикл корутин

Корутина проходит через несколько состояний в течение своего жизненного цикла:

1. Создание: Корутина создается с помощью строителей, таких как launch или async.

2. Запуск: Корутина начинает выполнение, когда её запускают с помощью start() или автоматически, если используется launch.

3. Приостановка: Корутина может быть приостановлена при вызове suspend-функции, например, delay() или await().

4. Возобновление: После завершения асинхронной операции корутина возобновляет выполнение с места приостановки.

5. Завершение: Корутина завершает выполнение, когда весь код внутри неё выполнен или когда она отменена.

Что такое Continuation

Continuation — это объект, который представляет собой состояние выполнения корутины и позволяет возобновить её выполнение после приостановки (это как бы callback, который вызывается, когда корутина готова продолжить выполнение после приостановки).

Continuation — это механизм, который позволяет Kotlin Coroutines приостанавливать и возобновлять выполнение. Он является ключевым элементом реализации suspend-функций и асинхронного программирования в Kotlin.

Когда корутина приостанавливается (например, при вызове suspend-функции), её состояние сохраняется в объекте Continuation. Этот объект содержит информацию о том, где именно корутина была приостановлена, и как её можно продолжить после завершения асинхронной операции.

В Kotlin Continuation — это интерфейс, который выглядит следующим образом:

interface Continuation<in T> {     val context: CoroutineContext     fun resumeWith(result: Result<T>) }

Пример.

Для следующего кода:

suspend fun mySuspendFunction() {     println("Start")     delay(1000)      println("End") }     launch {         mySuspendFunction()      }

Код будет состоять из двух частей:

  1. println(«Start») и delay(1000) (весь код до первой suspend функции и сама функция);

  2. println(«End») (весь оставшийся код, т.к. больше нет suspend функций).

Создастся следующий класс (код компилируется в байт-код, код на java привожу для упрощения):

class GeneratedContinuation extends SuspendLambda {     int label; // Текущее состояние        @Override     Object invokeSuspend(Object result) {         switch (label) {             case 0:                 println("Start");                 label = 1;                 if (delay(1000, this) == COROUTINE_SUSPENDED)                      return COROUTINE_SUSPENDED; // Приостановка                 // После возобновления переходит к case 1             case 1:                 println("End");                 return Unit.INSTANCE;         }         throw IllegalStateException();     } }

Метка label — это указатель на текущую точку выполнения внутри корутины, он используется для отслеживания текущего состояния корутины. При каждом вызове suspend-функции значение label изменяется, чтобы указать, с какого места нужно продолжить выполнение после приостановки.

При первом вызове (label = 0) выполняется код до delay().

delay(1000, this) передаёт Continuation (this) в suspend функцию, для того, чтобы после окончания своей работы она вызвала invokeSuspend и работа продолжилась. При приостановке возвращается COROUTINE_SUSPENDED. COROUTINE_SUSPENDED — это специальное значение, которое возвращается, когда корутина приостанавливается. Это является сигналом для механизма корутин: «Выполнение приостановлено, нужно освободить текущий поток».

После истечения времени приостановки, т.е. после завершения suspend функции работа продолжается со следующим значением label. В нашем случае по окончанию delay(1000) выполнение продолжается с label = 1.

Возврат значения suspend функцией

Для возврата значений у нас есть в функции invokeSuspend параметр result.

Рассмотрим пример

launch {     val data = fetchData() // suspend fun      processData(data) // suspend fun      println("Data is processed") }

Код будет состоять из трёх частей:

  1. fetchData() (весь код до первой suspend функции и сама функция);

  2. processData(data) (весь код после первой suspend функции и по вторую suspend функцию включительно);

  3. println(«Data is processed») (весь оставшийся код, т.к. больше нет suspend функций).

class GeneratedContinuation extends SuspendLambda {     int label; // Текущее состояние     byte[] data;        @Override     Object invokeSuspend(Object result) {         switch (label) {             case 0:                 label = 1;                 data = fetchData(this);                 return COROUTINE_SUSPENDED; // Приостановка                 // После возобновления переходит к case 1             case 1:                 data = (byte[]) result;                 label = 2;                 processData(data, this);                 return COROUTINE_SUSPENDED; // Приостановка                 // После возобновления переходит к case 2             case 2:                println("Data is processed");                 return Unit.INSTANCE;         }         throw IllegalStateException();     } }

Т.е. suspend функция fetchData после окончания работы вызовет invokeSuspend на «объекте корутины» и передаст в качестве параметра result результат своей работы — массив байт data.

Ссылка на документацию Kotlin.


ссылка на оригинал статьи https://habr.com/ru/articles/882988/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *