Controlo do Fluxo Assíncrono

O controlo do fluxo de JavaScript é tudo haver com a manipulação de função de resposta. Neste artigo apresentaremos estratégias que podem ajudar-te no teu desenvolvimento.

Autores do Artigo
Tabela de Conteúdos
Lembra-te de que este conteúdo é uma referência e pode ficar desatualizado rapidamente. Nós recomendamos outras fontes como a MDN.

O material neste artigo é fortemente inspirada pelo Livro de Node.js do Mikito Takada.

No seu núcleo, a JavaScript é desenhada para ser não bloqueante na linha de processamento "principal", este é onde as visões são apresentadas. Tu podes imaginar a importância disto no navegador. Quando a linha de processamento principal é bloqueada resulta na infame "congelação" que os utilizadores finais receiam, e nenhum outro evento pode ser despachado resultando na perda de aquisição de dados, por exemplo.

Isto cria algumas restrições únicas que apenas um estilo função de programação pode curar. Isto é onde as funções de resposta entram em cena.

No entanto, as funções de resposta podem tornar-se desafiantes para manipular em procedimentos mais complicados. Isto resulta com frequência no "inferno de função de resposta" onde várias funções encaixadas com as funções de resposta tornam o código mais desafiantes de ler, depurar, organizar, etc.

Claro que, na vida real provavelmente existiriam mais linhas de código para lidar com result1, result2, etc., assim, o comprimento e complexidade deste problema normalmente resulta em código que parece muito mais desarrumando do que o exemplo acima.

Isto é onde as funções entram em excelente uso. Operações mais complexas são compostas de muitas funções:

  1. estilo inicializador / entrada
  2. intermediário
  3. terminador

O "estilo inicializador / entrada" é a primeira função na sequência. Esta função aceitará a entrada original, se houver algum, para a operação. A operação é uma sucessão de funções executáveis, e a entrada original será primariamente:

  1. variáveis em um ambiente global
  2. invocação direta com ou sem argumentos
  3. valores obtidos pelo sistema de ficheiro ou requisições de rede

As requisições de rede podem ser requisições de chegada inicializadas por uma rede estrangeira, por uma outra aplicação na mesma rede, ou pela própria aplicação na mesma rede ou na rede estrangeira.

Uma função intermediária retornará uma outra função, e uma função terminadora invocará a função de resposta. O exemplo a seguir ilustra o fluxo para as requisições de rede ou de sistema de ficheiro. Neste a latência é 0 porque todos estes valores estão disponíveis na memória.

Gestão de Estado

As funções pode ou não ser dependentes de estado. A dependência de estado aparece quando a entrada ou outra variável de uma função depende de uma função externa.

Desta maneira existem duas estratégias primarias para a gestão de estado:

  1. passando as variáveis diretamente para uma função, e
  2. adquirindo um valor de variável a partir da memória de consulta imediata, sessão, ficheiro, base de dados, rede, ou outra fonte externa.

Nota que, não mencionamos variáveis globais. A gestão de estado com variáveis globais é muitas vezes um antipadrão desleixado que torna difícil ou impossível garantir o estado. As variáveis globais em programas complexos devem ser evitadas quando possível.

Controlo do Fluxo

Se um objeto estiver disponível na memória, a iteração é possível, e não haverá uma mudança para controlo de fluxo:

No entanto, se os dados existem fora da memória a iteração não mais funcionará:

Porquê que isto aconteceu? A setTimeout instrui a CPU a armazenar as instruções noutro lado do autocarro, e instrui que o dado está agendado para ir buscar em um momento posterior. Milhares de ciclos de CPU chegam antes da função acessar novamente na marca de 0 milissegundo, a CPU busca as instruções do autocarro e executa-os. O único problema é que song ('') foi retornada milhares de ciclos prioritários.

A mesma situação surge na negociação com os sistemas de ficheiro e requisições de rede. A linha de processamento principal simplesmente não pode ser bloqueada por um período indeterminado de tempo-- portanto, usamos funções de resposta para agendar a execução do código em tempo de uma maneira controlada.

Tu serás capaz de realizar quase todas as tuas operações com o seguinte 3 padrões:

  1. Em sucessões: as funções serão executadas em uma ordem sequencial estrita, este é mais parecido com os laços de for:

  2. Paralelismo completo: quando a ordem não for um problema, tais como enviar por correio eletrónico uma lista de 1.000.000 de recetores de correio eletrónico.

  3. Paralelismo limitado: o paralelismo com limite, tal como enviar com êxito por correio eletrónico 1.000.000 recetores de uma lista de 10E7 utilizadores.

Cada um tem os seus próprios casos de uso, benefício, e problemas que podes experimentar e ler sobre com mais detalhes. Mais importante, lembre de modularizar as tuas operações e usar as funções de resposta! Se estiveres em dúvida, trate tudo como se fosse um intermediário.