Dart: Programação Assíncrona no Dart — Future / async-await
Última atualização: 2026-08-26
A programação assíncrona permite que seu programa não espere — uma operação de I/O de 3 segundos deixa a CPU fazer outro trabalho em vez de apenas esperar.
1. O que Você Aprenderá
- Ciclo de vida do Future: pendente / concluído / falhou
- Açúcar sintático async / await e tratamento de erros
- Composição de Futures: Future.wait / Future.any / Future.forEach
- Completer: controle manual da conclusão do Future
- Cenário do Bob: DataPipeline fazendo requisições concorrentes a múltiplas APIs de e-commerce
2. A História Real de um Desenvolvedor
(1) Dor: Requisições Sequenciais Causam Carregamento de 12 Segundos
O DataPipeline do Bob precisa buscar dados de 3 APIs: pedidos (3 segundos), produtos (2 segundos) e clientes (2 segundos). Ele inicialmente usou requisições sequenciais: 3 + 2 + 2 = 7 segundos. Adicionando a consulta ao banco de dados (5 segundos), toda a geração do relatório levou 12 segundos. A experiência do usuário era terrível, e os clientes SaaS reclamavam que era "muito lento."
(2) Solução: Execução Concorrente com Future.wait
Usar Future.wait faz com que as 3 requisições à API rodem concorrentemente, com o tempo total determinado pela mais lenta: 3 segundos. Combinado com consultas concorrentes ao banco de dados, a geração do relatório inteiro é reduzida para 4 segundos.
// Sequencial: 3 + 2 + 2 = 7 segundos
final orders = await fetchOrders(); // 3s
final products = await fetchProducts(); // 2s
final customers = await fetchCustomers(); // 2s
// Concorrente: max(3, 2, 2) = 3 segundos
final results = await Future.wait([
fetchOrders(),
fetchProducts(),
fetchCustomers(),
]);
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
(3) Benefícios
- Future.wait com execução concorrente reduz chamadas de API de 7 segundos para 3 segundos, total de 4 segundos.
- async/await faz o código assíncrono parecer síncrono, melhorando a legibilidade.
- Tratamento de erros robusto garante que exceções de rede não mais crashem o programa.
3. Fundamentos do Future
(1) Ciclo de Vida do Future
sequenceDiagram participant Bob participant API1 participant API2 participant API3 Bob->>API1: fetchOrders() Bob->>API2: fetchProducts() Bob->>API3: fetchCustomers() Note over Bob: Future.wait concorrente API1-->>Bob: 1.2M pedidos API2-->>Bob: 500K produtos API3-->>Bob: 300K clientes Bob->>Bob: mesclar & analisar
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Criando e Usando um Future
import 'dart:async';
// Criar um Future que completa após um atraso
Future<String> fetchOrder() async {
await Future.delayed(const Duration(seconds: 2));
return 'ORD-001: \$1500.00 USD';
}
// Future com erro
Future<String> fetchOrderWithRetry() {
return Future.delayed(const Duration(seconds: 1), () {
throw Exception('Tempo limite de rede esgotado');
});
}
void main() async {
// Pendente → Concluído
print('Buscando pedido...');
final order = await fetchOrder();
print(order);
// Pendente → Falhou
try {
await fetchOrderWithRetry();
} catch (e) {
print('Erro: $e');
}
}
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
| Estado do Future | Significado |
|---|---|
| pendente | Incompleto, aguardando |
| concluído com valor | Concluído com sucesso, carregando um valor |
| concluído com erro | Concluído com falha, carregando uma exceção |
4. async / await em Profundidade
(1) Sintaxe Básica
▶ Exemplo
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Fundamentos de async/await
import 'dart:async';
Future<double> calculateOrderTotal(String orderId) async {
// await pausa a execução até o Future completar
final amount = await fetchAmount(orderId);
final taxRate = await fetchTaxRate(orderId);
return amount * (1 + taxRate);
}
Future<double> fetchAmount(String orderId) async {
await Future.delayed(const Duration(milliseconds: 500));
return 1500.0;
}
Future<double> fetchTaxRate(String orderId) async {
await Future.delayed(const Duration(milliseconds: 300));
return 0.08;
}
void main() async {
final total = await calculateOrderTotal('ORD-001');
print('Total: \$${total.toStringAsFixed(2)} USD');
}
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
(2) Tratamento de Erros
▶ Exemplo
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Tratamento de Erros com async/await
import 'dart:async';
Future<String> fetchApiData(String endpoint) async {
await Future.delayed(const Duration(seconds: 1));
if (endpoint.contains('invalid')) {
throw Exception('Erro de API: $endpoint não encontrado');
}
return 'Dados de $endpoint';
}
Future<void> robustFetch(String endpoint) async {
try {
final data = await fetchApiData(endpoint);
print('Sucesso: $data');
} on Exception catch (e) {
print('Exceção: $e');
} finally {
print('Busca concluída para $endpoint');
}
}
void main() async {
await robustFetch('orders'); // Sucesso
await robustFetch('invalid-endpoint'); // Exceção
}
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
| Método de Tratamento de Erros | Sintaxe | Caso de Uso |
|---|---|---|
| try-catch | try { await f(); } catch (e) {} |
Quando recuperação é necessária |
| catchError | f().catchError((e) => ...) |
Estilo funcional |
| onError | f().then(..., onError: ...) |
Callbacks simples |
5. Composição de Futures
(1) Future.wait — Espera Concorrente
▶ Exemplo
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Future.wait Concorrente
import 'dart:async';
Future<List<String>> fetchOrders() async {
await Future.delayed(const Duration(seconds: 3));
return ['ORD-001', 'ORD-002', 'ORD-003'];
}
Future<List<String>> fetchProducts() async {
await Future.delayed(const Duration(seconds: 2));
return ['Laptop', 'Mouse', 'Keyboard'];
}
Future<List<String>> fetchCustomers() async {
await Future.delayed(const Duration(seconds: 2));
return ['Alice', 'Bob', 'Charlie'];
}
void main() async {
// Concorrente: max(3, 2, 2) = ~3 segundos
final stopwatch = Stopwatch()..start();
final results = await Future.wait([
fetchOrders(),
fetchProducts(),
fetchCustomers(),
]);
stopwatch.stop();
print('Pedidos: ${results[0]}');
print('Produtos: ${results[1]}');
print('Clientes: ${results[2]}');
print('Tempo: ${stopwatch.elapsedMilliseconds}ms');
}
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Tratamento de Erros com Future.wait
import 'dart:async';
Future<String> riskyFetch(String name, bool shouldFail) async {
await Future.delayed(const Duration(seconds: 1));
if (shouldFail) throw Exception('$name falhou');
return 'Dados de $name';
}
void main() async {
// Future.wait falha rápido - se qualquer um falhar, tudo falha
try {
await Future.wait([
riskyFetch('API-1', false),
riskyFetch('API-2', true), // Este vai falhar
riskyFetch('API-3', false),
]);
} catch (e) {
print('Future.wait falhou: $e');
}
// Preservar resultados individuais com eagerError: false
final results = await Future.wait(
[
riskyFetch('API-1', false).then((v) => Result.success(v)),
riskyFetch('API-2', true).then((v) => Result.success(v)).catchError((e) => Result.failure(e.toString())),
riskyFetch('API-3', false).then((v) => Result.success(v)),
],
);
for (final r in results) {
print(r.isSuccess ? 'OK: ${r.data}' : 'FALHA: ${r.error}');
}
}
class Result<T> {
final T? data;
final String? error;
final bool isSuccess;
Result.success(this.data) : error = null, isSuccess = true;
Result.failure(this.error) : data = null, isSuccess = false;
}
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
(2) Future.any — Corrida
▶ Exemplo
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Future.any Corrida
import 'dart:async';
Future<String> fetchFromCache() async {
await Future.delayed(const Duration(milliseconds: 100));
return 'Cache: Dados ORD-001';
}
Future<String> fetchFromApi() async {
await Future.delayed(const Duration(seconds: 2));
return 'API: Dados ORD-001';
}
Future<String> fetchFromDb() async {
await Future.delayed(const Duration(milliseconds: 500));
return 'DB: Dados ORD-001';
}
void main() async {
// Retorna o PRIMEIRO a completar
final fastest = await Future.any([
fetchFromCache(),
fetchFromApi(),
fetchFromDb(),
]);
print('Mais rápido: $fastest'); // Cache: Dados ORD-001
}
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
(3) Future.forEach — Iteração Sequencial
▶ Exemplo
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Future.forEach
import 'dart:async';
Future<void> processOrder(String orderId) async {
await Future.delayed(const Duration(milliseconds: 500));
print('Processado: $orderId');
}
void main() async {
final orders = ['ORD-001', 'ORD-002', 'ORD-003'];
// Processar sequencialmente
await Future.forEach(orders, processOrder);
print('Todos os pedidos processados');
}
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
| Método de Composição | Comportamento | Concorrência | Caso de Uso |
|---|---|---|---|
Future.wait |
Esperar todos | Concorrente | Requisições em lote |
Future.any |
Primeiro a completar | Concorrente | Corrida/Fallback |
Future.forEach |
Conclusão sequencial | Sequencial | Operações dependentes de ordem |
6. Controle Manual com Completer
▶ Exemplo
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Completer
import 'dart:async';
// Controle manual de Future com Completer
class ApiService {
final Completer<String> _initCompleter = Completer<String>();
Future<String> get initialized => _initCompleter.future;
void onConnected(String serverInfo) {
if (!_initCompleter.isCompleted) {
_initCompleter.complete(serverInfo);
}
}
void onError(Object error) {
if (!_initCompleter.isCompleted) {
_initCompleter.completeError(error);
}
}
}
void main() async {
final service = ApiService();
// Simular inicialização assíncrona
Future.delayed(const Duration(seconds: 1), () {
service.onConnected('Servidor v3.0.1, 1.000.000 registros');
});
print('Aguardando inicialização...');
final info = await service.initialized;
print('Conectado: $info');
}
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
7. Cenário do Bob: Requisições Concorrentes à API
▶ Exemplo
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Busca Concorrente de Dados no DataPipeline
import 'dart:async';
// Chamadas de API simuladas
Future<List<Map<String, dynamic>>> fetchOrders() async {
await Future.delayed(const Duration(seconds: 2));
return [
{'id': 'ORD-001', 'amount': 1500.0, 'category': 'Electronics'},
{'id': 'ORD-002', 'amount': 3200.0, 'category': 'Electronics'},
{'id': 'ORD-003', 'amount': 890.0, 'category': 'Clothing'},
];
}
Future<Map<String, double>> fetchProductPrices() async {
await Future.delayed(const Duration(seconds: 1));
return {'Laptop': 1299.99, 'Mouse': 29.99, 'Keyboard': 79.99};
}
Future<List<String>> fetchCustomerNames() async {
await Future.delayed(const Duration(seconds: 1));
return ['Alice', 'Bob', 'Charlie'];
}
// Busca concorrente de dados com tratamento de erros
Future<void> generateReport() async {
final stopwatch = Stopwatch()..start();
try {
// Buscar todos os dados concorrentemente
final results = await Future.wait([
fetchOrders().then((v) => ('orders', v)).catchError((e) => ('orders', null)),
fetchProductPrices().then((v) => ('products', v)).catchError((e) => ('products', null)),
fetchCustomerNames().then((v) => ('customers', v)).catchError((e) => ('customers', null)),
]);
stopwatch.stop();
print('=== Relatório do DataPipeline ===');
print('Tempo de busca: ${stopwatch.elapsedMilliseconds}ms');
for (final (key, value) in results) {
if (value != null) {
print('$key: OK (${value is List ? value.length : value.length} itens)');
} else {
print('$key: FALHOU');
}
}
} catch (e) {
print('Falha na geração do relatório: $e');
}
}
void main() async {
await generateReport();
}
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
8. Exemplo Completo: Processamento Assíncrono de Dados no DataPipeline
// ============================================
// Processamento Assíncrono de Dados do DataPipeline
// Future, async/await e operações concorrentes
// ============================================
import 'dart:async';
// Fontes de dados simuladas
Future<List<Map<String, dynamic>>> fetchOrders({int delay = 2}) async {
await Future.delayed(Duration(seconds: delay));
return [
{'id': 'ORD-001', 'amount': 1500.0, 'status': 'completed', 'category': 'Electronics'},
{'id': 'ORD-002', 'amount': 50.0, 'status': 'completed', 'category': 'Books'},
{'id': 'ORD-003', 'amount': 3200.0, 'status': 'pending', 'category': 'Electronics'},
{'id': 'ORD-004', 'amount': 890.0, 'status': 'completed', 'category': 'Clothing'},
];
}
Future<List<Map<String, dynamic>>> fetchProducts({int delay = 1}) async {
await Future.delayed(Duration(seconds: delay));
return [
{'name': 'Laptop', 'price': 1299.99, 'category': 'Electronics'},
{'name': 'Mouse', 'price': 29.99, 'category': 'Electronics'},
{'name': 'Novel', 'price': 12.99, 'category': 'Books'},
];
}
Future<double> fetchTaxRate(String region) async {
await Future.delayed(const Duration(milliseconds: 500));
return switch (region) {
'US' => 0.08,
'EU' => 0.20,
'UK' => 0.15,
_ => 0.10,
};
}
// Pipeline assíncrono com operações concorrentes
class AsyncPipeline {
final String name;
String status = 'idle';
AsyncPipeline({required this.name});
Future<Map<String, dynamic>> run({String region = 'US'}) async {
status = 'running';
final stopwatch = Stopwatch()..start();
try {
// Passo 1: Busca concorrente de dados
final (orders, products, taxRate) = await (
fetchOrders(),
fetchProducts(),
fetchTaxRate(region),
).wait;
// Passo 2: Processar pedidos
final completedOrders = orders
.where((o) => o['status'] == 'completed')
.toList();
// Passo 3: Calcular receita
final revenue = completedOrders.fold<double>(
0, (sum, o) => sum + (o['amount'] as double));
final taxAmount = revenue * taxRate;
final totalWithTax = revenue + taxAmount;
// Passo 4: Agrupar por categoria
final byCategory = <String, double>{};
for (final order in completedOrders) {
final cat = order['category'] as String;
final amt = order['amount'] as double;
byCategory.update(cat, (v) => v + amt, ifAbsent: () => amt);
}
stopwatch.stop();
status = 'completed';
return {
'pipeline': name,
'region': region,
'taxRate': taxRate,
'totalOrders': orders.length,
'completedOrders': completedOrders.length,
'revenue': revenue,
'tax': taxAmount,
'totalWithTax': totalWithTax,
'byCategory': byCategory,
'processingTime': stopwatch.elapsedMilliseconds,
'status': status,
};
} catch (e) {
status = 'failed';
rethrow;
}
}
}
void main() async {
final pipeline = AsyncPipeline(name: 'E-Commerce Analytics');
print('=== Iniciando DataPipeline ===');
final report = await pipeline.run(region: 'US');
print('\n=== Relatório ===');
print('Pipeline: ${report['pipeline']}');
print('Região: ${report['region']}');
print('Taxa Imposto: ${(report['taxRate'] as double * 100).toStringAsFixed(1)}%');
print('Total Pedidos: ${report['totalOrders']}');
print('Concluídos: ${report['completedOrders']}');
print('Receita: \$${(report['revenue'] as double).toStringAsFixed(2)} USD');
print('Imposto: \$${(report['tax'] as double).toStringAsFixed(2)} USD');
print('Total: \$${(report['totalWithTax'] as double).toStringAsFixed(2)} USD');
print('Tempo: ${report['processingTime']}ms');
print('\nPor Categoria:');
for (final entry in (report['byCategory'] as Map<String, double>).entries) {
print(' ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
}
}
> **Saída:** Execute no DartPad ou usando `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
Saída:
=== Iniciando DataPipeline ===
=== Relatório ===
Pipeline: E-Commerce Analytics
Região: US
Taxa Imposto: 8.0%
Total Pedidos: 4
Concluídos: 3
Receita: $2440.00 USD
Imposto: $195.20 USD
Total: $2635.20 USD
Tempo: 2000ms
Por Categoria:
Electronics: $1500.00 USD
Books: $50.00 USD
Clothing: $890.00 USD
❓ Perguntas Frequentes
P: Qual é o tipo de retorno de uma função async? R: Uma função async automaticamente envolve o valor de retorno em um Future. Declare um tipo de retorno
Future<T>e simplesmente retorne T dentro da função.
P: await só pode ser usado em funções async? R: Sim. await só pode ser usado em funções async ou geradores async*. Dart 3.5+ suporta await de nível superior (apenas em Dart scripts).
P: O que acontece se um Future falhar em Future.wait? R: Por padrão, se qualquer Future falhar, Future.wait imediatamente lança essa exceção (os outros Futures continuam executando mas seus resultados são ignorados). Você pode usar
eagerError: falsepara esperar que todos completem.
P: Qual é a diferença entre async/await e .then()? R: Eles são funcionalmente equivalentes, mas async/await é mais legível (fluxo linear), enquanto .then() é mais funcional (encadeamento). Para uso diário, async/await é recomendado; .then() é adequado para transformações simples.
P: Quando devo usar Completer? R: Use Completer quando precisar controlar manualmente o momento de conclusão de um Future. Cenários comuns: converter callbacks de eventos em Futures, mensagens WebSocket, callbacks de bibliotecas de terceiros. Na maioria dos casos, você pode simplesmente usar async/await.
P: Future é multicast ou unicast? R: Um Future é unicast — só pode ser consumido por um await ou .then(). Para multicast, você precisa de um Stream ou asStream().
P: Como implementar controle de timeout? R: Use o método Future.timeout(). Por exemplo:
await fetchApi().timeout(Duration(seconds: 5), onTimeout: () => defaultData).
📖 Resumo
- Um Future representa o resultado eventual de uma computação assíncrona, com três estados: pendente/concluído/falhou.
- async/await é açúcar sintático para Future, fazendo código assíncrono ler como código síncrono.
- Future.wait executa múltiplos Futures concorrentemente, com o tempo total determinado pelo mais lento.
- Future.any corre para obter o mais rápido; Future.forEach executa sequencialmente.
- Completer controla manualmente a conclusão do Future, adequado para converter callbacks em Futures.
📝 Exercícios
- Básico (Dificuldade ⭐): Escreva 3 funções que retornam Futures (cada uma com um atraso de 1/2/3 segundos). Chame-as sequencialmente usando async/await e meça o tempo total. Depois chame-as concorrentemente usando Future.wait e compare os tempos.
- Intermediário (Dificuldade ⭐⭐): Implemente uma função de requisição assíncrona com retry:
fetchWithRetry(url, retries: 3, backoff: Duration). Ela deve tentar novamente em caso de falha com backoff exponencial e lançar uma exceção após exceder o limite de retries. - Desafio (Dificuldade ⭐⭐⭐): Implemente um protocolo simples de "requisição-resposta" usando Completer: envie uma requisição e aguarde a resposta correspondente, com suporte a timeout. Simule um barramento de mensagens onde múltiplas requisições podem ser enviadas concorrentemente.