Design Pattern com Async/Await em Node.js

Async/Await entrará no LTS esse ano, você também está animado?

Image for post
Image for post
Estamos chegando, e com tudo nativo!

Async/Await chegou no Node.js por padrão, e com isso, traz um poderoso design para nosso código. Processos que necessitavam bibliotecas complexas ou chamadas múltiplas linkando Promises, hoje, podem ser feitas com declarações básicas como if e for. Já falamos sobre esses padrões com CO, porém, async/await torna esses padrões acessíveis usando apenas Node.js, sem necessitar de outras bibliotecas!

Request falhou? Tente novamente!

O poder do await é que ele permite que você escreva código assíncrono usando construtores síncronas da linguagem. No exemplo abaixo, iremos tentar fazer o request HTTP novamente caso ele falhe, vamos usar a biblioteca superagent usando o bom e velho callback:

Nada muito difícil, mas isso envolve recursão e pode ser difícil para iniciantes no projeto. Além do mais, tem um pequeno problem escondido aqui. O que acontece quando superagent.get().end() retornar um excessão síncrona?

Nós precisamos envolver nosso _request em uma chamada try..catch para realmente tratar todas as excessões. Ter isso em todo os lugares para uma única requisição, pode ser custoso e propenso a erro. Com async/await, você pode escrever uma função com a mesma funcionalidade com apenas um for e try..catch.

Confie em mim, isso funciona! E muito bem! :)

Eu me lembro da primeira vez que eu tentei esse padrão com co. Tenha que te dizer que fiquei surpreso que tinha funcionado. Porém, o exemplo abaixo não funciona. Lembre-se, await obrigatoriamente precisa estar em uma função async. A closure passada para o forEach não é async:

Trabalhando com MongoDB Cursor

A função find() do MongoDB retorna um Cursor. Um Cursor é básicamente um objeto que tem uma função assíncrona next(), que busca o próximo documento do resultado da query executada.

Se não houver mais resultados, next() irá ser resolvido para null. MongoDB Cursor possui várias funções utilitárias como each(), map() e toArray(). O mongoose ODM adiciona uma outra função chamada eachAsync(), que na verdade é apenas açúcar sintático em cima da função next().

Sem async/await, chamar next() manualmente, envolve o mesmo tipo de recursão que vimos no exemplo anterior. Com async/await, você vai perceber que você não mais vai usar as funções utilitárias (provavelmente nenhuma outra além de toArray()). Isso tudo porque iterar sobre o cursor com um for é bem mais simples:

Se isso não é conveniente o suficiente para você. Saiba que existe uma proposta no TC39 para iteradores assíncronos. O exemplo abaixo não funciona em nenhuma versão atual do Node.js, é apenas um exemplo do que essa nova proposta quer implementar:

Múltiplos request em paralelo

Os dois exemplos acima executam os requests em sequência, existe apenas um next() executando em determinado momento. E como ficam os request múltiplos? Vamos imaginar que você é um hacker que quer fazer o hash de múltiplas senhas em paralelo com bcrypt:

A função Promise.all() recebe um array de promises e retorna uma promise que irá esperar que cada promise passada no array seja finalizada e, por fim, irá retornar um array que contém o resultado de cada uma, na sequência que as requisições foram passdas.

Cada bcrypt.hash() retorna uma promise, então promises no array acima contém um array de promises, e o valor de await Promise.all(promises) é o resultado de cada chamada do bcrypt.hash().

Promise.all() não é a única maneira de cuidar de múltiplos requests em paralelo, existe também o Promise.race(), que executa múltiplas promises em paralelo, espera apenas uma única promise ser resolvida e retorna o valor dela. Aqui um exemplo usando Promise.race() com async/await:

Perceba que, Promise.race() é resolvida quando um único resultado dos múltiplos requests é retornado. Os outros requests irão continuar sua execução, porque Promises não são canceláveis.

Seguindo em frente

Async/Await é uma grande conquista para o JavaScript. Com essas duas simples palavras chaves, você pode remover verbosidade, dependências externas e números de linhas da sua base de código.

Você pode adicionar um tratamento de erro robusto, realizar múltiplas tentativas, paralelização, tudo isso, com os mais simples operadores da linguagem.

Eu espero que você esteja animado assim como eu estou, principalmente porque irá chegar ao Node.js 8 LTS, aparentemente em Abril de 2017!

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store