---
title: "Coroutines em Kotlin Tutorial Básico em Português — Passo a Passo | Kotlin Brasil"
url: "https://kotlin.dev.br/tutoriais/coroutines-tutorial-basico/"
markdown_url: "https://kotlin.dev.br/tutoriais/coroutines-tutorial-basico.MD"
description: "Aprenda Coroutines em Kotlin do zero: suspend functions, launch, async/await, dispatchers, structured concurrency e cancelamento neste tutorial."
date: "2025-07-01"
author: "Karina Melo"
---

# Coroutines em Kotlin Tutorial Básico em Português — Passo a Passo | Kotlin Brasil

Aprenda Coroutines em Kotlin do zero: suspend functions, launch, async/await, dispatchers, structured concurrency e cancelamento neste tutorial.


Neste tutorial, você vai aprender os fundamentos de **Coroutines em Kotlin** — o mecanismo oficial da linguagem para programação assíncrona e concorrente. Coroutines permitem escrever código assíncrono de forma sequencial e legível, sem callbacks aninhados ou complexidade desnecessária. Ao final, você vai dominar suspend functions, `launch`, `async`/`await`, dispatchers, structured concurrency e os conceitos básicos de cancelamento.

## O que São Coroutines?

Uma [coroutine](/glossario/coroutine/) é uma instância de computação suspensível. Diferente de threads do sistema operacional, coroutines são extremamente leves — você pode criar milhares delas sem problemas de performance. Enquanto uma thread bloqueada consome recursos do sistema, uma coroutine suspensa libera a thread para fazer outro trabalho.

Pense em coroutines como funções que podem **pausar** sua execução em pontos específicos e **retomar** mais tarde, possivelmente em outra thread. Isso é perfeito para operações de I/O (rede, disco, banco de dados) que passam a maior parte do tempo esperando.

## Configurando o Projeto

Antes de usar coroutines, você precisa adicionar a dependência no seu `build.gradle.kts`:

```kotlin
dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
    // Para Android, adicione tambem:
    // implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
}
```

## Suspend Functions

Uma [suspend function](/glossario/suspend/) é uma função que pode ser pausada e retomada. Ela é marcada com a palavra-chave `suspend`:

```kotlin
import kotlinx.coroutines.*

suspend fun buscarDadosDoServidor(): String {
    delay(2000) // Simula uma chamada de rede (2 segundos)
    return "Dados carregados com sucesso!"
}

suspend fun salvarNoCache(dados: String) {
    delay(500) // Simula escrita no cache
    println("Cache atualizado: $dados")
}
```

A função `delay()` é o equivalente suspensível de `Thread.sleep()` — ela pausa a coroutine sem bloquear a thread. Suspend functions só podem ser chamadas de dentro de outras suspend functions ou de coroutine builders.

O importante é que suspend functions se parecem com código síncrono normal. Não há callbacks, não há `.then()`, não há observables encadeados. O código é lido de cima para baixo, como deveria ser.

## runBlocking

O `runBlocking` é o ponto de entrada mais simples para o mundo das coroutines. Ele bloqueia a thread atual até que todas as coroutines internas terminem:

```kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    println("Início: ${Thread.currentThread().name}")

    val dados = buscarDadosDoServidor()
    println(dados)

    salvarNoCache(dados)

    println("Fim!")
}

// Saída:
// Início: main
// Dados carregados com sucesso!
// Cache atualizado: Dados carregados com sucesso!
// Fim!
```

Use `runBlocking` apenas em funções `main()` e em testes. Em código de produção (especialmente Android), você vai usar coroutine scopes apropriados.

## launch: Coroutines Fire-and-Forget

O builder [launch](/glossario/launch/) cria uma nova coroutine que roda de forma concorrente. Ele retorna um `Job` que pode ser usado para controlar a coroutine:

```kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    println("Antes do launch")

    val job = launch {
        println("Coroutine iniciada")
        delay(1000)
        println("Coroutine finalizada")
    }

    println("Depois do launch — a coroutine está rodando em paralelo!")

    job.join() // Espera a coroutine terminar
    println("Tudo concluído")
}

// Saída:
// Antes do launch
// Depois do launch — a coroutine está rodando em paralelo!
// Coroutine iniciada
// Coroutine finalizada
// Tudo concluído
```

Veja como várias coroutines rodam concorrentemente:

```kotlin
fun main() = runBlocking {
    val inicio = System.currentTimeMillis()

    val job1 = launch {
        delay(1000)
        println("Tarefa 1 completa")
    }

    val job2 = launch {
        delay(1500)
        println("Tarefa 2 completa")
    }

    val job3 = launch {
        delay(800)
        println("Tarefa 3 completa")
    }

    // Todas rodam em paralelo!
    job1.join()
    job2.join()
    job3.join()

    val tempo = System.currentTimeMillis() - inicio
    println("Total: ${tempo}ms") // ~1500ms, NÃO 3300ms!
}
```

As três tarefas executam simultaneamente, então o tempo total é determinado pela tarefa mais lenta (1500ms), não pela soma de todas.

## async/await: Coroutines com Retorno

Enquanto `launch` é para tarefas "fire-and-forget", [async](/glossario/async/) é para quando você precisa de um resultado. Ele retorna um `Deferred<T>`, e você obtém o valor com `await()`:

```kotlin
import kotlinx.coroutines.*

suspend fun buscarUsuario(): String {
    delay(1000)
    return "Ana Silva"
}

suspend fun buscarPedidos(): List<String> {
    delay(1500)
    return listOf("Pedido #1", "Pedido #2", "Pedido #3")
}

fun main() = runBlocking {
    val inicio = System.currentTimeMillis()

    // Executar as duas buscas em paralelo
    val usuarioDeferred = async { buscarUsuario() }
    val pedidosDeferred = async { buscarPedidos() }

    // Esperar os resultados
    val usuario = usuarioDeferred.await()
    val pedidos = pedidosDeferred.await()

    println("Usuário: $usuario")
    println("Pedidos: $pedidos")

    val tempo = System.currentTimeMillis() - inicio
    println("Total: ${tempo}ms") // ~1500ms (em paralelo)
}
```

Se as chamadas fossem sequenciais, levariam 2500ms (1000 + 1500). Com `async`, levam apenas ~1500ms porque rodam em paralelo. Isso é extremamente valioso em aplicações reais onde você precisa buscar dados de múltiplas fontes.

## Coroutine Scope

Todo coroutine builder (`launch`, `async`) opera dentro de um [scope](/glossario/scope/). O scope define o ciclo de vida das coroutines:

```kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    // runBlocking cria um CoroutineScope

    // coroutineScope cria um sub-scope que espera todas as coroutines filhas
    coroutineScope {
        launch {
            delay(500)
            println("Tarefa 1 do scope")
        }
        launch {
            delay(300)
            println("Tarefa 2 do scope")
        }
        println("Dentro do coroutineScope")
    }

    println("Depois do coroutineScope — todas as filhas terminaram")
}

// Em codigo real (como no Android):
class MinhaViewModel : ViewModel() {
    // viewModelScope é cancelado quando o ViewModel é destruído
    fun carregarDados() {
        viewModelScope.launch {
            val dados = buscarDadosDoServidor()
            // Atualizar UI
        }
    }
}
```

O `coroutineScope` é uma suspend function que cria um novo escopo e só retorna quando todas as coroutines filhas terminam. Se qualquer filha falhar, todas as outras são canceladas automaticamente.

## Dispatchers

Dispatchers determinam em qual thread (ou pool de threads) a coroutine será executada:

```kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    // Dispatchers.Default — para trabalho intensivo de CPU
    launch(Dispatchers.Default) {
        println("Default: ${Thread.currentThread().name}")
        // Cálculos pesados, processamento de dados, algoritmos
        val resultado = (1..1_000_000).sum()
        println("Soma: $resultado")
    }

    // Dispatchers.IO — para operacoes de I/O
    launch(Dispatchers.IO) {
        println("IO: ${Thread.currentThread().name}")
        // Chamadas de rede, leitura/escrita de arquivos, banco de dados
        delay(100) // Simula I/O
    }

    // Dispatchers.Main — para atualizar a UI (Android)
    // launch(Dispatchers.Main) {
    //     textView.text = "Atualizado!"
    // }

    // Dispatchers.Unconfined — sem thread especifica (raro de usar)
    launch(Dispatchers.Unconfined) {
        println("Unconfined: ${Thread.currentThread().name}")
    }
}
```

Na prática, o padrão mais comum é:

```kotlin
// Padrão Android típico
viewModelScope.launch {  // Main por padrao no Android
    val dados = withContext(Dispatchers.IO) {
        // Executar chamada de rede na thread de IO
        api.buscarDados()
    }
    // De volta na Main thread — seguro para atualizar UI
    atualizarTela(dados)
}
```

A função `withContext()` troca o dispatcher temporariamente e retorna ao dispatcher original quando termina.

## Structured Concurrency

Structured concurrency é o princípio de que coroutines formam uma hierarquia pai-filho. Quando o scope pai é cancelado, todas as coroutines filhas são canceladas automaticamente:

```kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        // Coroutines filhas
        launch {
            repeat(10) { i ->
                println("Filha 1: iteração $i")
                delay(500)
            }
        }

        launch {
            repeat(10) { i ->
                println("Filha 2: iteração $i")
                delay(300)
            }
        }
    }

    delay(1200) // Deixa rodar por 1.2 segundos
    println("Cancelando o pai...")
    job.cancelAndJoin() // Cancela o pai E todas as filhas
    println("Todas as coroutines foram canceladas")
}
```

Isso garante que não existam coroutines "órfãs" rodando sem controle. É uma das vantagens mais importantes das coroutines sobre threads tradicionais.

## Cancelamento de Coroutines

Coroutines suportam cancelamento cooperativo. Funções suspensíveis da stdlib (`delay`, `yield`, `withContext`) verificam automaticamente se a coroutine foi cancelada:

```kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        try {
            repeat(100) { i ->
                println("Processando item $i...")
                delay(200) // Ponto de cancelamento
            }
        } catch (e: CancellationException) {
            println("Coroutine cancelada! Limpando recursos...")
        } finally {
            println("Finally: cleanup executado")
        }
    }

    delay(1000)
    println("Solicitando cancelamento...")
    job.cancelAndJoin()
    println("Concluído")
}
```

Para código que faz computação intensiva sem chamar funções suspensíveis, use `isActive` ou `ensureActive()`:

```kotlin
val job = launch(Dispatchers.Default) {
    var i = 0
    while (isActive) { // Verifica se a coroutine ainda está ativa
        i++
        // Trabalho intensivo de CPU
        if (i % 1000 == 0) println("Iteração $i")
    }
    println("Loop encerrado em $i")
}

delay(100)
job.cancelAndJoin()
```

## Exemplo Prático Completo

Vamos simular um cenário real — carregar dados de múltiplas fontes em paralelo:

```kotlin
import kotlinx.coroutines.*

data class DashboardData(
    val usuario: String,
    val notificacoes: List<String>,
    val estatisticas: Map<String, Int>
)

suspend fun buscarPerfil(): String {
    delay(800)
    return "Diego Oliveira"
}

suspend fun buscarNotificacoes(): List<String> {
    delay(1200)
    return listOf("Nova mensagem", "Pedido enviado", "Promoção ativa")
}

suspend fun buscarEstatisticas(): Map<String, Int> {
    delay(1000)
    return mapOf("visitas" to 1500, "vendas" to 42, "avaliações" to 230)
}

suspend fun carregarDashboard(): DashboardData = coroutineScope {
    val perfilDeferred = async { buscarPerfil() }
    val notificacoesDeferred = async { buscarNotificacoes() }
    val estatisticasDeferred = async { buscarEstatisticas() }

    DashboardData(
        usuario = perfilDeferred.await(),
        notificacoes = notificacoesDeferred.await(),
        estatisticas = estatisticasDeferred.await()
    )
}

fun main() = runBlocking {
    val inicio = System.currentTimeMillis()

    val dashboard = carregarDashboard()

    println("Usuário: ${dashboard.usuario}")
    println("Notificações: ${dashboard.notificacoes}")
    println("Estatísticas: ${dashboard.estatisticas}")

    val tempo = System.currentTimeMillis() - inicio
    println("Carregado em ${tempo}ms") // ~1200ms (máximo das 3)
}
```

Três chamadas que somariam 3000ms sequencialmente executam em apenas ~1200ms em paralelo.

## Erros Comuns

1. **Usar `runBlocking` em código de produção**: `runBlocking` bloqueia a thread atual. Em Android, usá-lo na Main thread causará ANR (Application Not Responding). Use `viewModelScope.launch` ou `lifecycleScope.launch` em vez disso.

2. **Esquecer de tratar `CancellationException`**: se você captura `Exception` genérica dentro de uma coroutine, pode acidentalmente engolir o `CancellationException` e impedir o cancelamento. Sempre relance `CancellationException` ou capture exceções mais específicas.

3. **Criar coroutines com `GlobalScope`**: `GlobalScope.launch` cria coroutines sem structured concurrency — elas não são canceladas quando a tela muda ou o componente é destruído. Isso causa memory leaks. Sempre use scopes apropriados.

4. **Não usar o dispatcher correto**: executar operações de I/O no `Dispatchers.Main` vai congelar a UI. Sempre use `Dispatchers.IO` para rede e disco, e `Dispatchers.Default` para computação pesada.

5. **Confundir `launch` com `async`**: use `launch` quando não precisa de retorno (fire-and-forget) e `async` quando precisa de um resultado. Usar `async` sem nunca chamar `await()` pode mascarar exceções.

## Conclusão e Próximos Passos

Coroutines são a forma idiomática de fazer programação assíncrona em Kotlin. Neste tutorial, você aprendeu suspend functions, `launch` e `async`/`await`, `runBlocking`, dispatchers, structured concurrency e cancelamento. Esses fundamentos são suficientes para começar a usar coroutines em projetos reais.

Para avançar no tema, explore:

- [Flow](/glossario/flow/) para streams de dados assíncronos
- [Channels](/glossario/channel/) para comunicação entre coroutines
- [Sealed Classes](/tutoriais/sealed-classes-tutorial/) para modelar estados de resultado de operações assíncronas
- [Lambdas](/tutoriais/lambdas-kotlin/) que são a base dos coroutine builders

A documentação oficial do Kotlin sobre coroutines é excelente e cobre cenários avançados como supervisão, exception handling e testing. Pratique refatorando callbacks existentes para coroutines e sinta a diferença na legibilidade do código. Para comparar modelos de concorrência, veja como <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go usa goroutines e channels para concorrência</a> e como <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust implementa async/await com o runtime Tokio</a>.
