Futures e tratamento de erros
Tudo que você queria saber sobre lidar com erros e exceções ao escrever código assíncrono. E um pouco mais.
A linguagem Dart tem suporte nativo
para assincronia,
tornando o código Dart assíncrono muito mais fácil de ler e escrever.
No entanto, algum código—especialmente código mais antigo—ainda pode usar
métodos de Future
como then(), catchError() e whenComplete().
Esta página pode ajudá-lo a evitar algumas armadilhas comuns ao usar esses métodos de Future.
A API Future e callbacks
#Funções que usam a API Future registram callbacks que lidam com o valor (ou o erro) que completa um Future. Por exemplo:
myFunc().then(processValue).catchError(handleError);
Os callbacks registrados disparam baseado nas seguintes regras: o
callback de then() dispara se ele é invocado em um Future que completa com um valor;
o callback de catchError() dispara se ele é invocado em um Future que completa
com um erro.
No exemplo acima, se o Future de myFunc() completa com um valor,
o callback de then() dispara. Se nenhum novo erro é produzido dentro de
then(),
o callback de catchError() não dispara. Por outro lado, se myFunc()
completa com um erro, o callback de then() não dispara, e
o callback de catchError() dispara.
Exemplos de uso de then() com catchError()
#
Invocações encadeadas de then() e catchError() são um padrão comum ao
lidar com Futures, e podem ser pensadas como o equivalente aproximado de
blocos try-catch.
As próximas seções dão exemplos deste padrão.
catchError() como um handler de erros abrangente
#
O exemplo a seguir lida com o lançamento de uma exceção de dentro do
callback de then() e demonstra a versatilidade de catchError()
como um handler de erros:
myFunc()
.then((value) {
doSomethingWith(value);
...
throw Exception('Some arbitrary error');
})
.catchError(handleError);
Se o Future de myFunc() completa com um valor, o callback de then() dispara. Se
o código dentro do callback de then() lança (como faz no exemplo acima),
o Future de then() completa com um erro. Esse erro é tratado por
catchError().
Se o Future de myFunc() completa com um erro, o Future de then() completa
com esse erro. O erro também é tratado por catchError().
Independentemente de o erro ter se originado dentro de myFunc() ou dentro de
then(), catchError() o trata com sucesso.
Tratamento de erros dentro de then()
#
Para tratamento de erros mais granular, você pode registrar um segundo callback (onError)
dentro de then() para lidar com Futures completados com erros. Aqui está
a assinatura de then():
Future<R> then<R>(FutureOr<R> Function(T value) onValue, {Function? onError});
Registre o callback onError opcional apenas se você quiser diferenciar
entre um erro encaminhado para then(), e um erro gerado dentro
de then():
asyncErrorFunction()
.then(
successCallback,
onError: (e) {
handleError(e); // Original error.
anotherAsyncErrorFunction(); // Oops, new error.
},
)
.catchError(handleError); // Error from within then() handled.
No exemplo acima, o erro do Future de asyncErrorFunction() é tratado com o
callback onError; anotherAsyncErrorFunction() faz com que o Future de
then() complete
com um erro; esse erro é tratado por catchError().
Em geral, implementar duas estratégias diferentes de tratamento de erros não é
recomendado: registre um segundo callback apenas se houver uma razão convincente
para capturar o erro dentro de then().
Erros no meio de uma cadeia longa
#
É comum ter uma sucessão de chamadas then(), e capturar erros
gerados de qualquer parte da cadeia usando catchError():
Future<String> one() => Future.value('from one');
Future<String> two() => Future.error('error from two');
Future<String> three() => Future.value('from three');
Future<String> four() => Future.value('from four');
void main() {
one() // Future completes with "from one".
.then((_) => two()) // Future completes with two()'s error.
.then((_) => three()) // Future completes with two()'s error.
.then((_) => four()) // Future completes with two()'s error.
.then((value) => value.length) // Future completes with two()'s error.
.catchError((e) {
print('Got error: $e'); // Finally, callback fires.
return 42; // Future completes with 42.
})
.then((value) {
print('The value is $value');
});
}
// Output of this program:
// Got error: error from two
// The value is 42
No código acima, o Future de one() completa com um valor, mas o Future de two()
completa com um erro. Quando then() é invocado em um Future que
completa com um erro, o callback de then() não dispara. Em vez disso,
o Future de then() completa com o erro de seu receptor. No nosso exemplo,
isso significa que após two() ser chamado, o Future retornado por cada
then() subsequente completa com o erro de two(). Esse erro é finalmente
tratado dentro de catchError().
Lidando com erros específicos
#E se quisermos capturar um erro específico? Ou capturar mais de um erro?
catchError() aceita um argumento nomeado opcional, test, que
nos permite consultar o tipo de erro lançado.
Future<T> catchError(Function onError, {bool Function(Object error)? test});
Considere handleAuthResponse(params), uma função que autentica um usuário
com base nos parâmetros fornecidos, e redireciona o usuário para uma URL apropriada.
Dado o fluxo de trabalho complexo, handleAuthResponse() pode gerar vários
erros e exceções, e você deve tratá-los de forma diferente. Veja como
você pode usar test para fazer isso:
void main() {
handleAuthResponse(const {'username': 'dash', 'age': 3})
.then((_) => ...)
.catchError(handleFormatException, test: (e) => e is FormatException)
.catchError(
handleAuthorizationException,
test: (e) => e is AuthorizationException,
);
}
Try-catch-finally assíncrono usando whenComplete()
#
Se then().catchError() espelha um try-catch, whenComplete() é o
equivalente de 'finally'. O callback registrado dentro de whenComplete()
é
chamado quando o receptor de whenComplete() completa, seja com um
valor ou com um erro:
final server = connectToServer();
server
.post(myUrl, fields: const {'name': 'Dash', 'profession': 'mascot'})
.then(handleResponse)
.catchError(handleError)
.whenComplete(server.close);
Queremos chamar server.close independentemente de server.post() produzir
uma resposta válida, ou um erro. Garantimos que isso aconteça colocando-o dentro
de whenComplete().
Completando o Future retornado por whenComplete()
#
Se nenhum erro é emitido de dentro de whenComplete(), seu Future completa
da mesma maneira que o Future em que whenComplete() é invocado. Isso é
mais fácil de entender através de exemplos.
No código abaixo, o Future de then() completa com um erro, então
o Future de whenComplete() também completa com esse erro.
void main() {
asyncErrorFunction()
// Future completes with an error:
.then((_) => print("Won't reach here"))
// Future completes with the same error:
.whenComplete(() => print('Reaches here'))
// Future completes with the same error:
.then((_) => print("Won't reach here"))
// Error is handled here:
.catchError(handleError);
}
No código abaixo, o Future de then() completa com um erro, que agora é
tratado por catchError(). Como o Future de catchError()
completa com
someObject, o Future de whenComplete() completa com esse mesmo objeto.
void main() {
asyncErrorFunction()
// Future completes with an error:
.then((_) => ...)
.catchError((e) {
handleError(e);
printErrorMessage();
return someObject; // Future completes with someObject
})
.whenComplete(() => print('Done!')); // Future completes with someObject
}
Erros originados dentro de whenComplete()
#
Se o callback de whenComplete() lança um erro, então o Future de
whenComplete() completa com esse erro:
void main() {
asyncErrorFunction()
// Future completes with a value:
.catchError(handleError)
// Future completes with an error:
.whenComplete(() => throw Exception('New error'))
// Error is handled:
.catchError(handleError);
}
Problema potencial: falha ao registrar handlers de erro cedo
#É crucial que handlers de erro sejam instalados antes de um Future completar: isso evita cenários onde um Future completa com um erro, o handler de erro ainda não está anexado, e o erro acidentalmente se propaga. Considere este código:
void main() {
Future<Object> future = asyncErrorFunction();
// BAD: Too late to handle asyncErrorFunction() exception.
Future.delayed(const Duration(milliseconds: 500), () {
future.then(...).catchError(...);
});
}
No código acima, catchError() não é registrado até meio segundo após
asyncErrorFunction() ser chamada, e o erro fica sem tratamento.
O problema desaparece se asyncErrorFunction() for chamada dentro do
callback Future.delayed():
void main() {
Future.delayed(const Duration(milliseconds: 500), () {
asyncErrorFunction()
.then(...)
.catchError(...); // We get here.
});
}
Problema potencial: misturando acidentalmente erros síncronos e assíncronos
#Funções que retornam Futures devem quase sempre emitir seus erros no futuro. Como não queremos que o chamador de tais funções tenha que implementar múltiplos cenários de tratamento de erros, queremos prevenir que qualquer erro síncrono vaze para fora. Considere este código:
Future<int> parseAndRead(Map<String, dynamic> data) {
final filename = obtainFilename(data); // Could throw.
final file = File(filename);
return file.readAsString().then((contents) {
return parseFileData(contents); // Could throw.
});
}
Duas funções nesse código podem potencialmente lançar sincronamente:
obtainFilename() e parseFileData(). Como parseFileData()
executa
dentro de um callback then(), seu erro não vaza para fora da função.
Em vez disso, o Future de then() completa com o erro de parseFileData(), o erro
eventualmente completa o Future de parseAndRead(), e o erro pode ser
tratado com sucesso por catchError().
Mas obtainFilename() não é chamado dentro de um callback then(); se ele
lançar, um erro síncrono se propaga:
void main() {
parseAndRead(data).catchError((e) {
print('Inside catchError');
print(e);
return -1;
});
}
// Program Output:
// Unhandled exception:
// <error from obtainFilename>
// ...
Como usar catchError() não captura o erro, um cliente de
parseAndRead() implementaria uma estratégia separada de tratamento de erros para este
erro.
Solução: Usando Future.sync() para envolver seu código
#
Um padrão comum para garantir que nenhum erro síncrono seja acidentalmente
lançado de uma função é envolver o corpo da função dentro de um novo callback Future.sync():
Future<int> parseAndRead(Map<String, dynamic> data) {
return Future.sync(() {
final filename = obtainFilename(data); // Could throw.
final file = File(filename);
return file.readAsString().then((contents) {
return parseFileData(contents); // Could throw.
});
});
}
Se o callback retorna um valor que não é Future, o Future de Future.sync() completa
com esse valor. Se o callback lança (como faz no exemplo
acima), o Future completa com um erro. Se o callback em si retorna um
Future, o valor ou o erro desse Future completa o Future de Future.sync().
Com o código envolvido dentro de Future.sync(), catchError() pode lidar com todos os erros:
void main() {
parseAndRead(data).catchError((e) {
print('Inside catchError');
print(e);
return -1;
});
}
// Program Output:
// Inside catchError
// <error from obtainFilename>
Future.sync() torna seu código resiliente contra exceções não capturadas. Se sua
função tem muito código embutido nela, as chances são de que você poderia estar fazendo
algo perigoso sem perceber:
Future fragileFunc() {
return Future.sync(() {
final x = someFunc(); // Unexpectedly throws in some rare cases.
var y = 10 / x; // x should not equal 0.
...
});
}
Future.sync() não apenas permite que você trate erros que você sabe que podem ocorrer, mas
também previne que erros acidentalmente vazem para fora de sua função.
Mais informações
#Veja a referência da API Future para mais informações sobre Futures.
Salvo indicação em contrário, a documentação neste site reflete Dart 3.13.7. Página atualizada pela última vez em 2026-11-12. Ver código-fonte ou relatar um problema.
Encontrou essa página sem tradução ou que precisa de correção? Abra uma issue e nos ajude a manter esse site em PT-BR para ajudar pessoas como você.