O bom e velho conceito assíncrono do Javascript

Nesta série de posts abordarei um velho problema entendimento no comportamento do processamento assíncrono em Javascript, na verdade, esse já é um assunto recorrente no stackoverflow e em comunidades para desenvolvedores novatos (ou não tão novatos assim) que não estão tão familiarizados com Javascrip por conta do sub-entendimento da natureza assíncrona da linguagem, no qual demanda uma forma um pouco diferente de se pensar e construir um sistema.

Para entender o problema, primeiramente devemos entender como o javascript interpreta suas instruções e o que são valores futuros, ou seja, dados que dependem de um processo assíncrono, no qual você não tem controle do tempo de resposta.

Comportamento do Javascript sobre a ordem das coisas

O Javascript, ao contrario do PHP, Ruby, C#, etc. não é preemptivo, traduzindo, ele não espera um determinado processo ou retorno de uma função pra dar continuidade ao bloco, e em termos de performance isso é bom, pois, ele é capaz de continuar a execução de outras tarefas, mesmo que ele não termine de executar uma determinada função anterior, e isso torna o javascript ainda mais atraente, embora traga com sigo uma outra forma de pensar no seu código.

Em PHP se você observar, a execução do trecho acima, o carregamento ocorrerá em ordem, mesmo com a função sleep() que adia o processo do próximo bloco. Esse comportamento vale também pra comunicação com o banco de dados e outras coisas, nas quais não se tem o controle de tempo de resposta. Embora seja um processo que não temos o controle do tempo de resposta, o PHP interpreta como um processo síncrono, ou seja, não precisamos nos preocupar com o que vem depois, pois uma pilha só é executada após a execução do método anterior. Porém esse cenário é um pouco diferente em Javascript, pois exceto que você use alguma estratégia pra lidar com dados assíncronos, seu comportamento não será top down linear quanto aos retornos, com exceção as novas especificações de de Sync mode no Node Js, que gerou uma grande polêmica.

Em javascript pra você ter esse resultado não basta fazer isso:

Porque o javascript não espera a chamada assíncrona pra te dar o retorno de uma função, ou seja, mesmo que você tenha uma ação assíncrona, o sistema tenta interpretar o que ele já tem, em resumo, embora seja uma leitura top down também, ao contrario do PHP e outras linguagens, ele não espera o termino de uma operação pra continuar a outra.

O Problema do setTimeout pra adiar o processamento assíncrono:

Toda vez que você usa um setTimeout para esse tipo de coisa, você está jogando na loteria, pois, está usando uma suposição para adivinhar o tempo que a máquina do seu cliente vai demorar para trazer o resultado da operação ou requisição. Com isso, você não ganha apenas um problema, você leva logo dois problemas para casa, que são os clientes que vão demorar mais do que você tinha previsto, e adiamento da interação ou navegação de um usuário que processou a operação antes da sua estimativa.

“Toda vez que você usa um timeout pra adiar um processo assíncrono, um programador se mata!”

Como computação é uma ciência exata e não esotérica, você deve assumir que não é um adivinho. Deixa as previsões para o João Bidu e comece a usar os recursos da linguagem, diga-se de passagem, ela te proporciona muitas alternativas para lidar com esse tipo de cenário.

Uma luz no fim do túnel

Para lidar com esses problemas, basicamente existem duas saídas, que são trabalhar com eventos ou com callbacks, ambos possuem caraterísticas muito diferentes entre si, mas, ambas resolvem algo em comum que é prover o controle sobre a execução das suas operações assíncronas. 
No caso do callback, esse controle se dará pela passagem de funções como parâmetro para controlar a ordem de execução do seu script, já os eventos, funcionam como uma estação de rádio que emite um sinal onde outras funções podem escutar e acompanhar as suas atividades e mudanças de estado. Extrapolando um pouco o conceito, e ainda nessa linha de raciocínio, temos o conceito de observer, que é um pattern, onde temos objetos emissores e observadores de estado, ou seja, um listener, no caso de evento, um listener é um observer por definição, mas, para se trabalhar com observers, você não precisa necessariamente da interface de eventos, assim como você pode utilizar outros conceitos pra lidar com comportamento assíncrono como os paterns Chain of Responsibility para encadeamento de ações ordenadas; O patern pub/sub para lidar com emissores e receptores de ações, onde você quer ter agentes que reagem as mudanças que o emissor emite; os signals pra lidar com comunicação bidirecional entre objetos, onde um emissor é também um receptor e vice versa; E as conhecidas Promises que são objetos com valores conhecidos mas não no momento que são criadas, ou seja, ele representará a promessa. que em algum momento trará uma resposta de sucesso ou falha. Mas calma, irei explicar cada um dos conceitos individualmente acima, suas vantagens e desvantagens, quando e como usar.

No meu próximo post abordarei tudo sobre Callbacks, e depois darei continuidade na explicação dos demais conceitos para que você entenda de uma vez por todas como trabalhar com comportamento assíncrono!