Kotlin Suspending Computations — Parte II

Jeziel Lago
4 min readMar 8, 2020

Continuando a nossa busca por descobrir como as suspending computations funcionam por “debaixo dos panos”, iniciamos a parte II desta série 🙌🏼. Nesse artigo, veremos como o compilador do Kotlin transforma em máquina de estado as suspending functions.

Caso você ainda não tenha lido a Parte I dessa série (Kotlin Suspending Computations — Parte I), basta voltar rapidinho e ler o artigo anterior, antes de continuar essa leitura 😬.

Suspending Functions & State Machine

Como vimos na parte I, o compilador do Kotlin transforma o nosso código escrito com o modificador suspend, em funções que usam Continuation.

Além disso, o código das suspending computations é modificado para usar máquina de estado, permitindo que as funções usem continuations para executar partes das instruções com base em seu estado atual. Isso significa que uma suspending function pode “pausar” a sua execução enquanto espera alguma outra operação terminar, e em um determinado momento no futuro, retomar a sua execução de onde tinha parado anteriormente.

Para entender melhor como tudo isso funciona usando máquina de estado, vamos analisar o mesmo trecho de código que vimos no artigo anterior.

Repare que a IDE nos mostra na lateral esquerda (com as flechas), os pontos de suspensão da função getUserProfile.

Para organizar a execução e utilizar máquina de estado nessa função, o compilador separa as instruções dentro da função em vários blocos. Para identificar cada bloco, são adicionados alguns labels (identificadores) com base nos pontos em que a execução da função pode ser retomada.

Imaginando esse passo em nossa função, teríamos algo como:

O compilador cria esses labels para separar qual parte do código deve ser executada em determinado momento. Sendo assim, a função acima poderia ser escrita usando um when (ou um switch), por exemplo.

Veja a seguir, uma representação de alto nível do que o compilador faz:

O código acima é apenas um pseudo-código para melhorar a compreensão.

Cada vez em que o código dentro de cada case do when é executado, o valor da variável label é atualizado, servindo assim para manter um estado consistente indicando qual será o próximo trecho de código que será executado. Dessa maneira, mesmo que a função seja suspensa é possível retomar a sua execução em um momento no futuro, exatamente onde ela parou anteriormente.

Explorando um pouco mais na prática

No artigo anterior, foi citado que nenhuma função suspend chama outra diretamente. Também é dito que o compilador altera as nossas suspending functions mudando o seu retorno para um java.lang.Object e além disso, adiciona um argumento a mais na função, um Continuation.

Bytecode decompilado mostrando como ficou a assinatura das três suspending functions apresentadas.

Mas… por quê? 🤔

  1. As funções recebem o objeto continuation como argumento, principalmente para recuperar o resultado de outras funções (como callbacks) e para conseguir pausar ou retomar a execução da função. O Continuation é usado para armazenar os retornos e controlar o fluxo de execução (e assim atualiza o valor da label para o próximo estado).
  2. As funções retornam um java.lang.Object porque podem devolver objetos diferentes quando forem chamadas. Ao invés de ser executada e já retornar, elas retornam um continuation que contém uma função "invoke" a qual permite que em um determinado momento no futuro seja chamada e execute o que de fato a sua função precisa fazer.

Na prática, teremos algo semelhante ao código abaixo:

Lembrando novamente que essa é apenas uma representação “pseudo Kotlin” do código gerado pelo compilador e diversos detalhes foram omitidos para tornar o exemplo mais didático.

A imagem abaixo ilustra brevemente o controle exercido pelo continuation no fluxo de execução:

Recomendo a leitura do artigo Kotlin Coroutines: Suspending Functions and State Machines para uma visão mais detalhada.

Você pode explorar mais o conteúdo dito aqui usando o seu próprio código! Abra a sua IDE, busque por “Show Kotlin Bytecode” e clique no botão DECOMPILE 😎.

No próximo artigo falaremos sobre mudança de contextos em coroutines, continuando a nossa análise sobre como as coisas funcionam por dentro. Aguardo você!

Se esse artigo agregou alguma coisa em seu conhecimento, não deixe de compartilhar! Obrigado por ler até aqui!😁👍🏻

Referências:

--

--