JavaScript: Tratando erros ao estilo Go

Como async/await nos ajuda a encapsular o nosso código

Image for post
Image for post
Cada linguagem com uma natureza diferente, será que é possível alguma semelhança?

A linguagem Go está cada vez mais em evidência. Uma das coisas que eu mais gosto nessa linguagem é a simplicidade do retorno de erros/respostas de funções que causam efeitos colaterais (side-effects).

Um exemplo bem bacana do pacote net/http:

Ou do pacote os, para lidar com sistema operacional:

  • [A]: cenário de erro da nossa função
  • [B]: cenário de sucesso da nossa função

Deixando os detalhes da linguagem de lado, afinal, aquilo ali é uma requisição assíncrona, certo? 😬

Será que poderíamos ter um estilo similar de respostas na nossa linguagem favorita?

Cenários em JavaScript

Historicamente falando, temos alguns cenários bem comuns em JavaScript para lidar com funções assíncronas:

Callbacks

Em Callbacks é comum passar uma função para o cenário de erro e sucesso:

Ao executarmos a função main(), temos um pequeno controle de quando podemos executar um código que depende do resultado dessa requisição. Obrigatoriamente precisamos colocar esse código dentro das funções das chaves success ou error.

Promises

Em Promises usamos o conceito de encadeamento de funções, utilizando os métodos .then e .catch:

Utilizando a função fetch, retornamos uma Promise na execução da função main() (perceba que estamos nomeando res ao invés de data, por padrão, fetch não irá processar a resposta para você). Novamente, temos um pequeno controle de quando podemos executar um código que depende do resultado dessa requisição. Obrigatoriamente precisamos colocar esse código dentro das funções .then ou .catch.

Error-first callbacks

Em Node.js, um cenário bem comum é retornar algum tipo de erro como o primeiro argumento da função Callback:

Como nos cenário anteriores, temos um pequeno controle de quando podemos executar um código que depende do resultado dessa operação.

async/await

Introduzidos no ECMAScript 2017, as palavras chaves async e await, podem transformar declarações, expressões, definições ou funções em linha em um novo objeto onde uma Promise é sempre retornada.

Utilizando o exemplo de Promise acima, podemos atualizar nossa sintaxe:

Um pouco melhor, não é? O código parece mais síncrono, ainda assim, temos que colocar o restante do nosso código dentro do try...catch e o tratamento de erro é separado da resposta de sucesso. Será que temos como melhorar isso?

Um pequeno detour

  • [A]: se você executar essa função (colocando uma URL válida) no Chrome DevTools, ela será executada sem problemas. Desde a versão 62, o Chrome suporta o chamado top-level await, onde você não precisa colocar uma função async em torno do await.

Porém, se quisermos executar esse código corretamente em um ambiente JavaScript/Node.js, precisamos de um pequeno truque:

Ficou na dúvida? Compartilhe sua perguntar abaixo ;)

Encapsulando async/await

Utilizando nosso último exemplo do async/await, será que conseguimos encapsular ainda mais a execução daquela requisição de rede?

O nosso objetivo final aqui é:

  • receber na função main() o objeto de resposta e erro
  • tratar o erro com um simples if
  • deixar nosso código o mais (visualmente) síncrono possível e legível (ou Go-like :P)

Como nossa função main() já está sendo executada como uma função async, nós podemos mover todo o try…catch da requisição para uma função de serviço.

Essa função de serviço será responsável por executar a requisição e retornar a dupla objeto de resposta e error em um formato padrão para nossa função main().

Podemos atualizar nosso código para:

Hooray!!! 🎉🤗🎉

Ficou BEM semelhante ao Go, não? Apesar desse exemplo ser simples em sua natureza, o padrão de encapsular funções de domínio e criar uma interface de resposta padronizada para o side-effect, não é nova!

É possível deixar o seu projeto Node.js mais limpo, enxugar o tamanho das suas redux-thunk ou redux-saga e isolar boa parte do uso de try…catch (se não todos) da sua aplicação. Uma convenção única em toda a stack!

Podemos recapitular a solução final em 3 etapas:

  • Transforme a operação em uma Promise 🙏
  • Encapsule com async/await e uma função de serviço 📦
  • Retorne o objeto de sucesso e erro de uma forma padronizada 🎹

Você que chegou até aqui, meu muito obrigado! 🤗

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