---
title: "Job em Kotlin: O que É e Como Funciona | Kotlin Brasil"
url: "https://kotlin.dev.br/glossario/job/"
markdown_url: "https://kotlin.dev.br/glossario/job.MD"
description: "Entenda o que é Job em Kotlin Coroutines, como controlar o ciclo de vida, cancelar e monitorar coroutines."
date: "2025-08-11"
author: "Karina Melo"
---

# Job em Kotlin: O que É e Como Funciona | Kotlin Brasil

Entenda o que é Job em Kotlin Coroutines, como controlar o ciclo de vida, cancelar e monitorar coroutines.


## O que é Job em Kotlin?

O **Job** é o elemento do contexto de uma coroutine que representa seu **ciclo de vida**. Ele permite controlar a execução da coroutine: verificar se esta ativa, esperar sua conclusão ou cancela-la. Todo `launch` retorna um Job, e todo `async` retorna um `Deferred` (que é um subtipo de Job com resultado).

Pense no Job como a "alca" que você segura para controlar uma coroutine que esta rodando em background.

### Sintaxe básica

```kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        delay(2000)
        println("Coroutine finalizada")
    }

    println("Job ativo? ${job.isActive}")     // true
    println("Job completo? ${job.isCompleted}") // false

    job.join() // Espera a coroutine terminar

    println("Job ativo? ${job.isActive}")     // false
    println("Job completo? ${job.isCompleted}") // true
}
```

O método `join()` suspende a coroutine atual até que o job termine. Diferente de `Thread.join()`, ele não bloqueia a thread.

### Estados do Job

Um Job passa por vários estados durante sua vida:

```
New -> Active -> Completing -> Completed
                     |
                     v
               Cancelling -> Cancelled
```

```kotlin
fun main() = runBlocking {
    val job = launch(start = CoroutineStart.LAZY) {
        println("Executando...")
        delay(1000)
        println("Finalizado")
    }

    println("Novo: isActive=${job.isActive}")         // false
    job.start()
    println("Ativo: isActive=${job.isActive}")        // true
    job.join()
    println("Completo: isCompleted=${job.isCompleted}") // true
}
```

Com `CoroutineStart.LAZY`, o job começa no estado "New" e só entra em "Active" quando você chama `start()` ou `join()`.

### Cancelamento de Jobs

O cancelamento e cooperativo em Kotlin. Chamar `cancel()` não mata a coroutine instantaneamente -- ela precisa verificar o cancelamento:

```kotlin
fun main() = runBlocking {
    val job = launch {
        repeat(1000) { i ->
            println("Trabalhando... $i")
            delay(100) // Ponto de suspensao: verifica cancelamento
        }
    }

    delay(500)
    println("Cancelando...")
    job.cancel()
    job.join() // Espera a coroutine finalizar o cancelamento
    // Ou: job.cancelAndJoin() // Combina cancel() + join()
    println("Cancelado")
}
```

Funções como `delay`, `yield` e `withContext` verificam o cancelamento automaticamente. Se sua coroutine faz trabalho intensivo de CPU sem pontos de suspensao, use `isActive` ou `ensureActive()`:

```kotlin
val job = launch(Dispatchers.Default) {
    var i = 0
    while (isActive) { // Verifica cancelamento manualmente
        i++
        // Trabalho intensivo de CPU
    }
    println("Parou em $i")
}
```

### Hierarquia de Jobs (Structured Concurrency)

Jobs formam uma hierarquia pai-filho. Quando uma coroutine lanca outra, o job filho e registrado no job pai:

```kotlin
fun main() = runBlocking {
    val jobPai = launch {
        val jobFilho1 = launch {
            delay(2000)
            println("Filho 1 finalizado")
        }
        val jobFilho2 = launch {
            delay(1000)
            println("Filho 2 finalizado")
        }
        println("Filhos lancados")
    }

    delay(500)
    jobPai.cancel() // Cancela pai E todos os filhos
    jobPai.join()
    println("Tudo cancelado")
}
```

Regras da hierarquia:
- Cancelar o pai cancela todos os filhos.
- Uma exceção não tratada em um filho cancela o pai e todos os irmaos.
- O pai só e considerado completo quando todos os filhos terminam.

### Job vs Deferred

`Deferred<T>` e um Job que retorna um resultado:

```kotlin
fun main() = runBlocking {
    val deferred: Deferred<Int> = async {
        delay(1000)
        42
    }

    // Deferred tem todas as funcionalidades de Job
    println("Ativo: ${deferred.isActive}")

    // Mais: pode obter o resultado
    val resultado = deferred.await()
    println("Resultado: $resultado")
}
```

Use `launch` (Job) quando não precisa de resultado; use `async` (Deferred) quando precisa.

### SupervisorJob

O `SupervisorJob` modifica o comportamento de propagacao de erros: a falha de um filho **não cancela os irmaos**:

```kotlin
fun main() = runBlocking {
    val supervisor = SupervisorJob()

    val scope = CoroutineScope(coroutineContext + supervisor)

    val job1 = scope.launch {
        delay(1000)
        throw RuntimeException("Erro no job 1")
    }

    val job2 = scope.launch {
        delay(2000)
        println("Job 2 finalizado normalmente") // Executa mesmo com erro no job1
    }

    delay(3000)
    supervisor.cancel()
}
```

SupervisorJob e essencial em cenários onde tarefas são independentes e a falha de uma não deve afetar as outras.

### Completable Job

`CompletableJob` e um Job que pode ser completado manualmente:

```kotlin
fun main() = runBlocking {
    val job = Job() // Cria um CompletableJob

    launch(job) {
        delay(1000)
        println("Tarefa executada")
    }

    job.complete() // Marca como completando (nao aceita mais filhos)
    job.join()     // Espera filhos existentes terminarem
    println("Tudo feito")
}
```

### Quando usar Job diretamente

- **Cancelamento controlado**: quando você precisa cancelar uma operação específica em resposta a uma acao do usuário.
- **Esperar conclusão**: quando uma parte do código depende de outra coroutine terminar antes de continuar.
- **Monitorar estado**: quando você precisa saber se uma coroutine ainda esta rodando.
- **Escopo customizado**: criar CoroutineScopes com Jobs específicos para controlar grupos de coroutines.

### Casos de Uso no Mundo Real

1. **Cancelamento de requisicoes de rede em Android**: Quando o usuário navega para outra tela, o Job associado ao CoroutineScope da tela anterior e cancelado, abortando automaticamente todas as chamadas de API em andamento. Isso evita vazamentos de memória e atualizações de UI em telas que já não existem.

2. **Processamento paralelo com controle de progresso**: Em aplicações que processam lotes de dados (como upload de múltiplas imagens), cada tarefa e lancada como um Job filho. O Job pai permite monitorar o progresso geral, cancelar tudo se necessário, e garantir que o processo só e considerado completo quando todas as imagens foram enviadas.

3. **Tarefas de background com timeout**: Servicos que consultam APIs externas utilizam Jobs com `withTimeout` para garantir que uma requisicao lenta não bloqueie o sistema indefinidamente. O Job e cancelado automaticamente apos o tempo limite, liberando recursos.

4. **Workers independentes com SupervisorJob**: Em servidores backend com Ktor ou Spring, cada requisicao HTTP e tratada por um Job independente sob um SupervisorJob. Se uma requisicao falha com exceção, as demais continuam operando normalmente sem serem afetadas.

### Boas Praticas

- Use `cancelAndJoin()` em vez de chamar `cancel()` e `join()` separadamente para garantir que o cancelamento seja concluido antes de prosseguir.
- Sempre verifique `isActive` ou chame `ensureActive()` em loops de CPU intensivo para que o cancelamento cooperativo funcione corretamente.
- Prefira `SupervisorJob` quando as coroutines filhas sao independentes e a falha de uma não deve afetar as demais.
- Nunca capture `CancellationException` em blocos `catch` genericos. Se precisar capturar `Exception`, relance `CancellationException` explicitamente.
- Estruture suas coroutines com escopos adequados (como `viewModelScope` ou `lifecycleScope` no Android) em vez de usar `GlobalScope`, para que os Jobs sejam gerenciados automaticamente pelo ciclo de vida.

### Perguntas Frequentes

**P: Qual a diferenca entre Job e Deferred?**
R: Job representa uma coroutine que executa uma tarefa sem retornar resultado, criado com `launch`. Deferred e um subtipo de Job que carrega um valor de retorno, criado com `async`. Voce obtém o resultado de um Deferred chamando `await()`, enquanto com Job você apenas espera a conclusao com `join()`.

**P: O que acontece quando um Job filho lanca uma exceção?**
R: Com um Job normal, a exceção propaga para o Job pai, que cancela todos os outros filhos e a si mesmo. Com SupervisorJob, a exceção fica isolada no filho que falhou, e os demais continuam executando normalmente.

**P: Por que o cancelamento de coroutines e cooperativo?**
R: O cancelamento cooperativo e uma decisao de design que evita problemas como corrupcao de dados e vazamento de recursos. Se uma coroutine fosse interrompida abruptamente, ela poderia deixar arquivos abertos ou transacoes incompletas. O modelo cooperativo permite que a coroutine finalize suas operações de limpeza antes de parar.

**P: Posso reutilizar um Job depois de cancelado?**
R: Nao. Um Job cancelado ou completado entra em um estado terminal e não pode ser reiniciado. Se você precisa executar a tarefa novamente, crie um novo Job com um novo `launch` ou `async`.

### Erros comuns

1. **Nao chamar join apos cancel**: `cancel()` apenas sinaliza o cancelamento. Sem `join()`, a coroutine pode continuar executando por um tempo. Use `cancelAndJoin()`.

2. **Ignorar a cooperatividade do cancelamento**: loops de CPU sem verificação de `isActive` ou pontos de suspensao não serao cancelados.

3. **Usar Job() como pai sem entender a propagacao**: um Job criado manualmente com `Job()` segue as regras normais de propagacao de erro. Para isolamento, use `SupervisorJob()`.

4. **Esquecer o tratamento de CancellationException**: quando um job e cancelado, `CancellationException` e lancada. Ela deve ser propagada, não capturada em `catch` generico.

5. **Nao estruturar coroutines**: lancar coroutines com `GlobalScope` em vez de usar escopos estruturados perde todo o beneficio de hierarquia de Jobs.

### Termos relacionados

- **Coroutine**: a unidade de execução cujo ciclo de vida o Job representa.
- **Deferred**: subtipo de Job que carrega um resultado, retornado por `async`.
- **SupervisorJob**: job especial onde falha de filhos não propaga para irmaos.
- **CoroutineScope**: escopo que contém um Job e define o ciclo de vida das coroutines.
- **Dispatcher**: trabalha junto com o Job no contexto da coroutine, determinando onde ela executa.
- **Structured Concurrency**: o princípio de que coroutines formam hierarquias através de Jobs.

O Job e a peça central do controle de ciclo de vida em Kotlin Coroutines. Dominar Jobs, cancelamento e hierarquia e fundamental para escrever código concorrente que seja robusto, previsivel e livre de vazamentos de recursos.
