async — é um Construtor de Corrotinas em Kotlin que inicia uma corrotina e retorna um objeto Deferred<T> para obter o resultado de uma operação assíncrona. async permite executar várias tarefas em paralelo e agregar seus resultados através de await(). De acordo com o guia JetBrains, 2024, async é a forma preferida de organizar cálculos paralelos em corrotinas.
Principais Pontos
async — é uma função de extensão do CoroutineScope que cria uma corrotina retornando um resultado. Ao contrário do launch, async retorna Deferred<T> — um objeto que representa um valor futuro. Uma corrotina iniciada via async executa concorrentemente com outras corrotinas, permitindo paralelizar cálculos.
async é usado quando o resultado de uma operação assíncrona é necessário para processamento posterior: carregar dados de várias fontes, processamento em lote de coleções, requisições paralelas a várias APIs. Deferred é criado imediatamente, e o resultado fica disponível após a conclusão da corrotina.
import kotlinx.coroutines.*
suspend fun String.delayedValue(): String {
delay(1000L)
return this
}
fun main() = runBlocking {
val deferred: Deferred<String> = async { "Hello".delayedValue() }
println(deferred.await())
}
A assinatura do async é semelhante ao launch: os mesmos parâmetros CoroutineContext, CoroutineStart e um bloco suspend. A diferença está no tipo de retorno — Deferred<T> em vez de Job, onde T é o tipo de resultado retornado pela última expressão do bloco.
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
Deferred<T> — uma interface que herda Job e adiciona os métodos await(), getCompleted(), getCompletionExceptionOrNull(). await() é uma função suspend e só pode ser chamada dentro de uma corrotina ou outra função suspend. getCompleted() é um método não bloqueante que lança uma exceção se a corrotina ainda não foi concluída.
A principal vantagem do async é a capacidade de executar tarefas em paralelo. Em vez de esperar cada operação sequencialmente (tempo total = soma dos tempos), async inicia corrotinas simultaneamente, reduzindo o tempo total para a duração da operação mais longa.
Um cenário típico — carregar o perfil do usuário, suas configurações e histórico de pedidos com três requisições async paralelas. Após a conclusão das três, os resultados são combinados em um único modelo de dados.
suspend fun loadUserDashboard(userId: String): Dashboard = coroutineScope {
val profile = async { api.getProfile(userId) }
val settings = async { api.getSettings(userId) }
val orders = async { api.getRecentOrders(userId) }
Dashboard(
profile = profile.await(),
settings = settings.await(),
orders = orders.await()
)
}
A função coroutineScope inicia corrotinas filhas e aguarda sua conclusão antes de retornar o resultado. Todo o bloco executa em paralelo, não sequencialmente.
Deferred suporta vários métodos para trabalhar com o resultado. Além de await(), há métodos para verificar o status, obter um valor concluído e agregar vários Deferreds através das funções utilitárias do Kotlin Coroutines.
| Método | Descrição | Suspend? |
|---|---|---|
| await() | Suspende a corrotina até obter o resultado | Sim |
| getCompleted() | Retorna o resultado se a corrotina foi concluída (senão Exception) | Não |
| getCompletionExceptionOrNull() | Retorna a exceção em caso de erro ou null | Não |
| isCompleted | Verifica se a corrotina foi concluída | Não |
Para agregar uma lista de Deferreds, usa-se awaitAll() — uma função que suspende a execução até que todos os Deferreds na coleção sejam concluídos. Se pelo menos um concluiu com erro, awaitAll() lança a exceção.
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
return coroutineScope {
val deferreds = requests.map { async { it() } }
deferreds.awaitAll()
}
}
A escolha entre async e launch depende se o resultado da corrotina é necessário. launch retorna Job e é adequado para operações fire-and-forget, async retorna Deferred e é usado para tarefas que retornam dados. Em termos de sintaxe e parâmetros, os construtores são idênticos.
Regra importante: nunca use async para fire-and-forget. Se o resultado da corrotina não for necessário, use launch. async cria sobrecarga ao criar Deferred, o que não se justifica nesses cenários.
No Android, async é usado para operações paralelas dentro de ViewModel e UseCases. Cenários típicos: carregar perfil e feed de notícias simultaneamente, obter clima de vários serviços meteorológicos, sincronização em lote de dados com o servidor.
class HomeViewModel : ViewModel() {
fun loadHomeScreen() {
viewModelScope.launch {
val userData = async(Dispatchers.IO) { repository.getUserData() }
val newsFeed = async(Dispatchers.IO) { repository.getNewsFeed() }
val notifications = async(Dispatchers.IO) { repository.getNotifications() }
val state = HomeState(
user = userData.await(),
news = newsFeed.await(),
unread = notifications.await()
)
_uiState.update { state }
}
}
}
É recomendado envolver um grupo de async em coroutineScope ou supervisorScope para isolamento de erros. supervisorScope permite que as demais corrotinas async continuem mesmo se uma falhar — útil para operações não críticas.
Perguntas Frequentes
Pode, mas é inútil. async sem await() inicia uma corrotina mas seu resultado é perdido. Se o resultado não for necessário, use launch — ele não cria Deferred e é mais eficiente em memória e desempenho.
Uma exceção dentro de async é armazenada em Deferred. Chamar await() lança essa exceção. Para verificar sem lançar, use getCompletionExceptionOrNull(). Ao usar coroutineScope, um erro cancela todas as corrotinas filhas.
awaitAll() — uma função de extensão para Iterable<Deferred<T>> que suspende a corrotina até que todos os Deferreds sejam concluídos. Retorna uma lista de resultados. Se pelo menos um Deferred falhar, lança a exceção e cancela os restantes.
Sim. async é uma função de extensão do CoroutineScope vinculada ao escopo. GlobalScope.async cria uma corrotina sem vinculação ao ciclo de vida — pode executar indefinidamente. GlobalScope não é recomendado no Android devido ao risco de vazamentos.
O número é ditado pelo dispatcher: Dispatchers.IO suporta até 64 threads simultâneos, Dispatchers.Default — até o número de núcleos de CPU. Para operações em massa, use paralelismo limitado via Semaphore ou mapNotNull com awaitAll.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também