Funções Assíncronas - Dart & Flutter
Como e quando utilizar: async | future | await | then
Introdução
Quando falamos sobre Flutter não podemos deixar de citar Dart, que é a linguagem utilizada pelo Framework/UIToolkit, e que assim como outras linguagens também trabalha com funções síncronas e assíncronas que é sobre o que vamos buscar entender aqui.
Explicando os dois tipos de funções
Explicando de uma forma mais grosseira:
Função Síncrona
bool tarefaFinalizada(index) { final tarefaFinalizada = tarefas[index].finalizada == true; return tarefaFinalizada; }
Uma função síncrona, assim que chamada, faz com que o sistema aguarde a execução de tudo que estiver dentro dela, para só depois continuar executando o restante das ações.
Podemos entender ela como uma função de certa forma "normal"
Função Assíncrona
Future<List<Tarefas>> getTarefas() async { // Vamos imaginar que o servidor demora 4s para retornar os dados. final listaDeTarefas = await http.get('tarefas'); return listaDeTarefas; }
Uma função assíncrona, assim que chamada, não é aguardada pelo sitema para que o mesmo de continuidade em seu fluxo, ele apenas continua fazendo o que precisa em paralelo.
Podemos entender ela como uma função que vai ser executada em paralelo com o resto do código sem travar nada e em algum momento no futuro vai retornar algum valor (Ou não).
Uso de algumas palavras-chaves
No caso das funções assíncronas, o Dart faz uso de algumas palavras-chave, que são:
async
Indica que a função vai ser assíncrona e em algum momento, dentro dela, talvez seja preciso esperar para pegar algum dado.
future
Informa que a função vai retornar um dado do futuro, necessário quando utilizamos a palavra-chave async.
Exemplo
Future<Exterminator> getExterminator() async { final buscarExterminador = await http.get('exterminator'); return buscarExterminador; }
Quando utilizamos funções assíncronas, precisamos utilizar o async e o Future para indicar que aquela função vai retornar um valor no "futuro", como por exemplo: Um retorno do servidor de SUCESSO ou ERRO após 5 segundos.
await
Faz com que o sistema espere naquela determinada linha até o término da função.
then
É uma função anônima (lambda) que só é executada quando a função assíncrona, a qual ela está vinculada, terminar.
Exemplo
Future<void> iniciarSkynet() async { final inicializacaoBemSucedida = await http.put('initialize', data: { "code": 0001 }); if (inicializacaoBemSucedida) { getExterminator().then((Exterminator exterminator){ exterminator.initializeProtocolZero(); print("inicializado") }); } print("log de inicialização") }
Utilizamos o await quando fazemos chamadas que retornem um Future e queremos que o sistema espere pelo retorno para que execute o resto das coisas.
Já o then usamos quando fazemos chamadas que retornem um Future, mas não queremos esperar seu resultado e sim executar alguma outra coisa apenas no final, deixando assim o fluxo seguir.
Casos de uso
Vamos entender na prática, quando utilizar cada uma das palavras-chave, utilizando um exemplo de login.
A ideia é que após preenhido os campos (Usuario e senha), a aplicação busque o usuário no servidor e retorne o nome do mesmo, com isso vamos poder saber se o usuário existe para permitir o acesso e caso não exista, podemos preenche-las no primeiro acesso.
Entenda cada expressão da seguinte forma:
- print(“ ”) como se fosse uma ação visual que está sendo exibida ao usuário em tela.
- Future.delayed() delays de conexão com o servidor.
Apenas para fins didáticos.
// Início do sistema
void main() {
fazerLoginComFeedback();
}
Future<void> fazerLoginComFeedback() async {
//Aguarda a função que fará contato com o servidor retornar o nome do usuário
String nomeUsuario = await getUsuario("matheus", "1234");
// Verifica se o usuário foi encontrado
if (nomeUsuario == "") {
print("Usuário não encontrado!");
} else {
//Exibe uma mensagem de boas-vindas com o nome do usuário retornado pelo servidor
exibirFeedbackVisual(nomeUsuario);
// Busca os dados do usuário do banco de dados e quando terminar exibe a tela principal
pegarDados(nomeUsuario).then((dados) {
print(dados);
// Após exibir a mensagem muda para a tela inicial
print("Abrindo tela inicial.");
});
}
}
// Função assíncrona
Future<String> getUsuario(String login, String senha) async {
print("Buscando usuário...");
// Requisição ao servidor para saber se o usuário existe
await Future.delayed(Duration(milliseconds: 2000));
// Usuário encontrado
String usuario = "Matheus Ribeiro";
return usuario;
}
// Função síncrona
void exibirFeedbackVisual(String nomeUsuario) {
print("Bem vindo, $nomeUsuario");
}
// Função assíncrona
Future<String> pegarDados(String usuario) async {
print("Buscando dados do $usuario...");
// Executa algumas consultas no banco para retornar os dados
await Future.delayed(Duration(milliseconds: 2000));
// Dados encontrados
String dadosUsuario = '{"id":1, "email": "[email protected]"}';
print("Armazena os dados em memória");
return dadosUsuario;
}
Caso queira testar o código acima, basta copiar e colar no Dartpad.
Como podemos ver, o grande foco do exemplo é o método fazerLoginComFeedback().
Dentro dele podemos ver as diferentes formas de tratar funções assíncronas, utilizando tanto o await quanto o then.
Ambos podem retornar o mesmo resultado, só que necessitam de tratamentos diferentes.
Podemos ver também a chamada da função getUsuario() utilizando o await, pois ali precisamos esperar o resultado da requisição para tomar alguma decisão no processo.
Já a chamada pegarDados() utilizamos o then porque essa função vai apenas buscar os dados no banco de dados e armazenar em memória para serem exibidos na próxima tela (Tela inicial)
FIM
Espero ter conseguido explicar de uma forma clara e objetiva.