Funções Assíncronas

Como e quando utilizar: async | future | await | then

Introdução

Dart, assim como outras linguagens, trabalha com funções síncronas e assíncronas.

Explicando de uma forma mais grosseira:

Função Síncrona

bool tarefaFinalizada(index) => 
    tarefas[index].finalizada == true;

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.


Função Assíncrona

Future<List<Tarefas>> getTarefas() async => 
    await http.get('tarefas');

É uma função que o sistema executa, mas não espera seu término, e continua fazendo o que precisa.

No caso das funções assíncronas, o Flutter (Dart) faz uso de algumas palavras-chave, que são:

async
Indica que a função é assíncrona e em algum momento talvez seja preciso esperar para pegar algum dado.


future
Essa função vai retornar um dado do futuro.

Future<Exterminator> getExterminator() async {
  return await http.get('exterminator');
}

Quando você usa funções assíncronas, você utiliza o Future para indicar que aquela função é assíncrona, ou melhor, que ela vai retornar um valor no "futuro", como por exemplo: SUCESSO ou ERRO após 5 segundos.


await
Espera o término da função.

Future<void> initializeSkynet() async => 
await http.put('initialize');

Você utiliza o await quando faz chamadas que retornem um Future. Isso faz com que o sistema espere a chamada da função assíncrona terminar para que execute o resto das coisas.


then
Execute essa chamada quando a função assíncrona terminar.

getExterminator().then((Exterminator exterminator){
exterminator.initializeProtocolZero();
});

Você utiliza o then quando faz chamadas que retornem um Future, mas não quer esperar seu resultado .
Dessa forma, todo o código continuará sendo executado enquanto a função assíncrona é executada também.

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 sabemos que ele existe e permitimos o acesso. Então buscamos as informações necessárias no banco de dados, e caso não existam, 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.
  • Future.delayed() delays de conexão com o servidor. Apenas para fins didáticos.
void main() {

  fazerLoginComFeedback();

}

String jsonDadosUsuario = "";

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;

}

void exibirFeedbackVisual(String nomeUsuario) {
  print("Bem vindo, $nomeUsuario");
}

Future<void> pegarDados(String usuario) async {
  print("Buscando dados do $usuario...");

  // Executa algumas consultas no banco para retornar os dados
  await Future.delayed(Duration(milliseconds: 2000));
  jsonDadosUsuario = '{"id":1, "email": "[email protected]"}';

}

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, enquanto os dados são carregados do banco de dados
     exibirFeedbackVisual(nomeUsuario);

     // Busca os dados e quando terminar exibe a tela principal
     pegarDados(nomeUsuario).then((value) { 

       // Após exibir a mensagem muda para a tela inicial
       print("Abrindo tela inicial.");

     });

   }
}

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 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 não vai nos retornar nada, vamos utilizar ela apenas para buscar os dados do banco e alimentar nossa variável jsonDadosUsuario, que por sua vez será usada na tela inicial para exibir todos os dados do usuário (Que caso ainda não existam, serão preenchidas no primeiro acesso).

FIM

Espero ter conseguido explicar de uma forma clara e objetiva.

23