Programação síncrona vs. assíncrona

Programação síncrona vs. assíncrona

Ao iniciar o estudo em alguma linguagem de programação, somos apresentados a dois conceitos muito importantes para estruturação de um código completo:: o código síncrono e o assíncrono.

Uma breve explicação, o código síncrono é lido e executado da primeira até a última linha, em ordem. O código  assíncrono, por sua vez, não necessariamente respeitará a ordem das linhas do código, podendo ter funções sendo lidas e executadas simultaneamente ou aleatoriamente.

Agora que entendemos o básico sobre esses conceitos, veremos mais detalhes neste artigo sobre suas diferenças, vantagens e desvantagens. Em seguida veremos alguns exemplos de como isso se aplica na prática.

Programação síncrona

A programação síncrona é o tipo padrão de execução do código e é o que vemos inicialmente nos nossos primeiros projetos. A sua principal vantagem é que é de fácil leitura e sua sintaxe é mais simples. Contudo, na programação síncrona, o código é interrompido enquanto o retorno da função não for concluído. Na prática, isso quer dizer que a experiência do usuário dentro da sua aplicação seria cortada ao iniciar uma execução demorada. A execução do código ficaria “travada” enquanto estivesse realizando qualquer função, sendo esta a  sua principal desvantagem.

Um exemplo disso é que, ao pesquisar ”celular” em uma página de e-commerce, a aplicação congelaria até que os produtos fossem renderizados, impedindo o usuário de abrir o menu, editar o carrinho de compras e navegar na página. O código síncrono não deve ser usado para funções que demoram certo tempo e não deve ser usado para a espera do retorno de servidores externos.

Com essa desvantagem que a programação síncrona traz, abriu-se espaço para o nosso segundo conceito, a programação assíncrona.

Programação assíncrona

Como falamos anteriormente, na  programação assíncrona o código não respeitará uma ordem hierárquica ou sequencial. Isto é, o código chamará a função, porém continuará executando as outras linhas enquanto espera a resposta da função. A assincronicidade é uma boa ideia quando a função provavelmente levará algum tempo para ser executada, como requisições de dados para o servidor ou como a função setTimeout do JavaScript.

Exemplificando para algo no cotidiano: Quando enviamos uma mensagem, podemos fazer outras coisas enquanto esperamos a resposta, como enviar outras mensagens, enviar vídeos, áudios ou até mesmo bloquear o usuário. Funciona como colocar algo que “escuta” quando a função recebe o retorno, porém, continua executando o restante do código. A partir do momento que a função recebe o retorno, ele faz o esperado pela funcionalidade. Caso a função que demora, fosse, por exemplo, carregar uma foto, você poderia enviar mais mensagens enquanto a foto ainda não carregou.

Abaixo temos um gráfico simples representando como seria o funcionamento síncrono e assíncrono em um site de e-commerce.


Como podemos ver no nosso exemplo, a programação assíncrona permite mais fluidez na experiência do usuário e agilidade na execução das funcionalidades da aplicação. Um código bem pensado é feito com a ideia de que a ordem de leitura do código não importe, o que, como consequência, garante um melhor desempenho quando estiver em execução.

Sabendo disso, fica claro que essa funcionalidade é extremamente útil, porém, como isso se aplica na prática? É o que veremos a seguir usando alguns exemplos em JavaScript.

Programação síncrona na prática

Criamos uma lógica simples que segue a execução do código na ordem em que foi escrita, fazemos uma soma, e então, escrevemos o resultado no console. Ao executar nosso código, o output sairá nessa ordem:

No primeiro “log” temos (a = 1, b = 2) , no segundo “log” temos (a = 3, b = 2), que, por fim, escrevem a soma das variáveis no terminal em ordem.

Com isso já entendemos o básico da execução do código e finalmente podemos seguir para a programação assíncrona.

Programação assíncrona na prática

No Javascript, temos algumas maneiras de transformar e tratar o código assíncrono: podemos usar de callbacks, funções assíncronas, promises ou  ‘async/await’. Todas essas aplicações têm suas respectivas vantagens e usos, podendo inclusive serem usadas em conjunto. Desta forma, partiremos para o exemplo:

Inicialmente, para entender o básico sobre assincronicidade, vamos usar a função setTimeout, que força que determinada função aguarde certo tempo antes de ser executada, simulando a demora em um retorno:

Ao executar o código, nosso output sairia assim:

Dessa vez, a função setTimeout executou o console.log somente 3 segundos após a sua chamada, e, portanto, a variável “a” já havia sido alterada para ”3” resultando em dois resultados em que (a= 3 e b = 2).

Isso, apesar de melhorar a execução do código, deixa as coisas um pouco confusas. Para facilitar o uso da execução assíncrona, surgiu a propriedade “async/await” que trata a execução assíncrona como à síncrona. Algo que ficará mais claro no exemplo do próximo tópico.

Como usar async / await?

Quando precisamos esperar um resultado para continuar a leitura do código, usamos o await, propriedade que aguarda o retorno da função mesmo que demore, para somente após o retorno continuar a execução do código.

Primeiramente vamos criar uma função assíncrona que retorna uma Promise e que demora 3 segundos para ser executada:

Antes de explicar como usamos o await e o async é importante entender o conceito e o funcionamento de uma “Promise”. Quando criamos uma “Promise”, resumidamente fazemos uma função que tem o seu retorno prometido, porém ainda não realizado. Um exemplo disso seria um restaurante que recebe o seu pedido, mas  só entrega o prato depois de um tempo, quando ficar pronto.

No nosso exemplo criamos a função “takesLong”.   Precisamos colocar a palavra “async” antes de chamá-la com “arrow function”. Essa palavra é usada para criar as funções assíncronas e, somente assim, podemos usar a propriedade await no nosso escopo. No caso acima, ao chamar a Promise, passamos uma função que demora 3 segundos para executar o parâmetro resolve, que faz o retorno da promise e por fim finaliza sua execução.

Depois disso foi feita a função “executionEnvironment”, função que servirá como ambiente de escopo. Quando chamamos uma função assíncrona com o await, garantimos que o código só terá continuidade quando tivermos o retorno da função, ou seja, como cada função demora 3 segundos para ser executada, o código deve demorar 6 segundos para ser lido por completo e o output sairá assim:

Até aqui aprendemos a usar promises e código assíncronos. Mas ainda ficamos com a dúvida de como fazer para que duas ou mais funções sejam executadas ao mesmo tempo? Para fazer isso, executaremos Promises simultaneamente. Veja mais abaixo.

Promises executadas simultaneamente

Executar Promises simultaneamente é ótimo quando fazemos requisições que não tem relação uma com a outra, isso reduz o tempo total de espera e melhora o fluxo da execução. Para começar, precisamos tirar o await da nossa função já que agora não iremos esperar o retorno. A função anterior ficará assim:

Ao tirarmos o await, a função retorna uma promessa de que o retorno chegará, o que fizemos foi guardar essa Promise em uma constante e usamos a funcionalidade “Promise.all()”, função que recebe um array, funcionando como uma lista e dispara todas as Promises dentro do array e aguarda seus retorno. Por fim usamos o “.then”, para escrever no terminal quando o processo do “Promise.all()” terminar corretamente.

O nosso output será:

Como podemos ver, foi na mesma ordem do anterior, porém demorou 3 segundos ao todo para executar o código, sendo 2 vezes mais rápido que o método anterior. Após entender essas funcionalidades já teremos a possibilidade de aplicar esses conceitos em nossas aplicações.

Os dois conceitos serão usados constantemente durante a nossa evolução como programadores, porém, quando devemos usar cada conceito? Após seguir os exemplos anteriores, chegamos na conclusão que se deve tratar uma função assíncrona, como síncrona, quando ela não interfere no funcionamento geral da aplicação.

A programação assíncrona, com Callbacks e Promises, deve ser usada para retornos que irão demorar certo tempo ou que afetam diretamente o funcionamento da aplicação. Sendo assim, o entendimento dessas formas de execução do código é muito importante e deve ser exercitado no dia a dia do desenvolvedor.

⚠️
As opiniões e comentários expressos neste artigo são de propriedade exclusiva de seu autor e não representam necessariamente o ponto de vista da Revelo.

A Revelo Content Network acolhe todas as raças, etnias, nacionalidades, credos, gêneros, orientações, pontos de vista e ideologias, desde que promovam diversidade, equidade, inclusão e crescimento na carreira dos profissionais de tecnologia.Jueves, 5 de enero