<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Danny</title>
    <description>The latest articles on DEV Community by Danny (@unimatrix2).</description>
    <link>https://dev.to/unimatrix2</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1776690%2Ff0c9fdc4-6eb0-4114-a57f-c263a0f3cdb7.jpeg</url>
      <title>DEV Community: Danny</title>
      <link>https://dev.to/unimatrix2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/unimatrix2"/>
    <language>en</language>
    <item>
      <title>Interlúdio Técnico #1 - OracleDB no JavaScript</title>
      <dc:creator>Danny</dc:creator>
      <pubDate>Sat, 31 Aug 2024 19:07:25 +0000</pubDate>
      <link>https://dev.to/unimatrix2/interludio-tecnico-1-oracledb-no-javascript-46cn</link>
      <guid>https://dev.to/unimatrix2/interludio-tecnico-1-oracledb-no-javascript-46cn</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Oi pessoal! Perdão o sumiço, como falei, ainda estou estabelecendo uma rotina que dá conta de tudo. Pensei muito em criar ou não esse tipo de artigo e decidi ir em frente. Os interlúdios técnicos serão artigos menores focados em soluções, dúvidas, descobertas pontuais que vão surgindo no dia-a-dia, como pequenas pílulas de conhecimento. Na nossa bolha de desenvolvedores temos muitas pessoas excelentes produzindo conteúdo de altíssima qualidade em formatos diferentes: seja no (Atualmente bloqueado) X/Twitter, BlueSky, Threads, aqui no dev.to, no YouTube e outras plataformas. Nesse momento não consigo acompanhar uma produção específica para uma plataforma, então vou escrevendo aqui e repostando nas redes, desculpem o transtorno e peço compreensão. Bora lá.&lt;/p&gt;

&lt;p&gt;Para esse primeiro interlúdio, decidi escrever sobre um querido do mundo &lt;em&gt;enterprise&lt;/em&gt;, um banco que para javeiros é mais um dia qualquer, mas pode assombrar devs em outras stacks. Hoje vou falar do OracleDB, ou mais especificamente como construir um backend ou função Lambda que utilize OracleDB em JavaScript/TypeScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que OracleDB?
&lt;/h2&gt;

&lt;p&gt;Não me pergunte, eu era uma criança quando isso ocorreu. O que posso dizer é que em mais de uma firma para qual trabalhei, os sistemas legados utilizam esse banco, e na modernização desses sistemas, seja uma refatoração apenas de código ou também de arquitetura, há alguns impecilhos para uma implementação de um backend JavaScript/TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drivers proprietários e o &lt;a href="https://www.npmjs.com/package/oracledb" rel="noopener noreferrer"&gt;node-oracledb&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Um banco Oracle em produção geralmente utiliza os recursos proprietários do banco para estabalecer conexões e dentre eles está o NNE (Native Network Encryption ou Criptograifa Nativa de Rede). Esse é o vilão da história, pois o node-oracledb traz apenas um cliente básico, ou como denominado pela prórpia Oracle, o thin-mode ou modo-magro na tradução ao pé da letra. Faz sentido o magro nesse contexto pois essa bilbioteca não traz o driver "cheio" da Oracle, que possui suporte ao NNE.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instalando o driver em um backend TypeScript/JavaScript
&lt;/h2&gt;

&lt;p&gt;Acredito que em um setup bem simples (como um sistema de arquivos ou armazenamento estático para guardar o código e uma instância de computação), os drivers podem ser copiados diretamente com o código fonte ou compilado e o carregamento do driver configurado para o caminho dos binários do driver, porém aqui vou trabalhar com uma solução baseada em Docker, pois vai garantir reprodutibilidade para deploys em Kubernetes, serviços Cloud baseados em Kubernetes, serviços cloud baseados em imagem Docker e instância de computação e serviços Cloud serverless que suportam imagens Docker.&lt;/p&gt;

&lt;p&gt;Para manuais, mais informações e documentação oficial, vou disponibilizar uma lista abaixo com os links mais úteis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/oracledb" rel="noopener noreferrer"&gt;NPM: node-oracledb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://node-oracledb.readthedocs.io/en/latest/user_guide/introduction.html#getstarted" rel="noopener noreferrer"&gt;Oracle: Introdução ao node-oracledb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/oracle/docker-images/tree/main/OracleInstantClient" rel="noopener noreferrer"&gt;Imagens Docker com o Oracle Instant Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/database/technologies/instant-client/downloads.html" rel="noopener noreferrer"&gt;Página de download do Oracle Instant Client&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Observações
&lt;/h3&gt;

&lt;p&gt;O nome oficial do driver "cheio" (thick-mode) da Oracle é Instant Client.&lt;br&gt;
Embora sejam disponiblizadas opções Docker com o Client pré-instalado, você pode encontrar problemas caso utilize uma Dockerfile multi-estágios, isso porque o Oracle Linux trabalha com binários compilados com MUSL, enquanto outras distribuições (inclusive as mais utilizadas para rodar o código final) utilizam GLIBC. Novamente, para fins de reprodutibilidade, demonstrarei aqui o caminho que em diferentes firmas foi a solução adotada, que consiste em baixar o Instant Client e copiá-lo para um diretório dentro da imagem Docker.&lt;/p&gt;
&lt;h3&gt;
  
  
  Dockerizando a aplicação
&lt;/h3&gt;

&lt;p&gt;Vamos iniciar com o caso mais simples: você possui a liberdade para utilizar o Oracle Linux. Vamos assumir um ponto de entrada index.js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; oraclelinux:8&lt;/span&gt;

&lt;span class="k"&gt;RUN  &lt;/span&gt;dnf &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;oracle-instantclient-release-el8 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    dnf &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;oracle-instantclient-basic oracle-instantclient-devel oracle-instantclient-sqlplus &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/cache/dnf

&lt;span class="k"&gt;RUN &lt;/span&gt;dnf &lt;span class="nt"&gt;-y&lt;/span&gt; module &lt;span class="nb"&gt;enable &lt;/span&gt;nodejs:18 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    dnf &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;nodejs npm &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/cache/dnf

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; PORT=3000&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; $PORT&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; [ "npm", "run", "start" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initOracleClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oracledb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;initOracleClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como a imagem já possui os caminhos (paths) todos preparados, esse setup já é suficiente para inicializar o driver.&lt;/p&gt;

&lt;p&gt;Agora, assumindo que não seja possível utilizar a imagem oraclelinux, o mesmo resultado pode ser atingido com um estágio para baixar o Instant Client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Neste estágio são instaladas as dependências do projeto&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;deps&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="c"&gt;# Neste, o projeto é compilado. A separação desse estágio da&lt;/span&gt;
&lt;span class="c"&gt;# instalação das dependências presume um processo de empacotamento&lt;/span&gt;
&lt;span class="c"&gt;# (bundling) do código com as dependências necessárias, de forma a&lt;/span&gt;
&lt;span class="c"&gt;# evitar uma pasta node_modules na imagem final&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=deps /app/node_modules ./node_modules&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;download&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# São instalados os pacotes necessários para baixar e extrair&lt;/span&gt;
&lt;span class="c"&gt;# os binários&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; wget unzip
&lt;span class="c"&gt;# A escolha de versão aqui (não é a última) se dá pela versão do&lt;/span&gt;
&lt;span class="c"&gt;# GLIBC utilizada&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;wget &lt;span class="nt"&gt;--no-cookies&lt;/span&gt; https://download.oracle.com/otn_software/linux/instantclient/2115000/instantclient-basic-linux.x64-21.15.0.0.0dbru.zip
&lt;span class="c"&gt;# Aqui é criado o diretório para expandir o arquivo .zip e é&lt;/span&gt;
&lt;span class="c"&gt;# executada a extração&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; instantclient
&lt;span class="k"&gt;RUN &lt;/span&gt;unzip instantclient-basic-linux.x64-21.15.0.0.0dbru.zip &lt;span class="nt"&gt;-d&lt;/span&gt; instantclient
&lt;span class="c"&gt;# Por final, o arquivo .zip é apagado&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm &lt;/span&gt;instantclient-basic-linux.x64-21.15.0.0.0dbru.zip
&lt;span class="c"&gt;# Todos os comandos aqui devem ser concatenadaos utilizando '&amp;amp;&amp;amp;'&lt;/span&gt;
&lt;span class="c"&gt;# e quebra de linha (\), optei pela verbosidade para comentar a&lt;/span&gt;
&lt;span class="c"&gt;# solução linha por linha&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runner&lt;/span&gt;

&lt;span class="c"&gt;# Aqui são configurados os caminhos de ambiente para o driver&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PORT=3000 PATH=$PATH:$ORACLE_HOME LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/oracle/instantclient_21_15&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; libc6-compat

&lt;span class="c"&gt;# Finalmente, o código compilado e empacotado é copiado para o&lt;/span&gt;
&lt;span class="c"&gt;# estágio final, bem como o Instant Client.&lt;/span&gt;
&lt;span class="c"&gt;# A escolha do diretório /opt se dá por uma convenção, pois é&lt;/span&gt;
&lt;span class="c"&gt;# o mesmo diretório utilizado am ambientes AWS Lambda para&lt;/span&gt;
&lt;span class="c"&gt;# carregar camadas e arquivos complementares&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/&amp;lt;seu diretório de output da build&amp;gt; ./&lt;/span&gt;
&lt;span class="c"&gt;# Como não existirá uma pasta node_modules no estágio final, são&lt;/span&gt;
&lt;span class="c"&gt;# copiados os binários do node-oracledb também&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/node_modules/oracledb/build /opt/build&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=download /app/instantclient_21_15 /opt/oracle/instantclient_21_15&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "run", "start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initOracleClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oracledb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;initOracleClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;binaryDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/opt/build/Release&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;libDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/opt/oracle/instantclient_21_15&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;A partir desse setup mínimo, seu serviço ou API está preparado para utilizar um banco Oracle com NNE, lembrando que o driver deve ser inicalizado antes de tentar se conectar ou formar uma piscina (pool) de conexões para utilizar o banco. Não desenvolvi uma solução de exemplo para este artigo pela diversidade de aplicações possíveis e suas bibliotecas e frameworks desenhados para cada tipo de solução, pessoalmente já desenvolvi tanto APIs REST quanto serviços engatilhados por eventos utilizando configurações parecidas.&lt;br&gt;
Nada nesse artigo é original: tudo pode ser encontrado em perguntas e respostas no StackOverflow, pode ser desenvolvido a partir de um rascunho de prompts de um ChatGPT ou Claude, minha missão aqui é produzir um ponto de partida em Português e condensar a experiência.&lt;/p&gt;

&lt;p&gt;Novamente, agradeço a leitura e a atenção! Até breve.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>node</category>
      <category>database</category>
    </item>
    <item>
      <title>Generator Functions, um caso de uso</title>
      <dc:creator>Danny</dc:creator>
      <pubDate>Tue, 06 Aug 2024 17:41:40 +0000</pubDate>
      <link>https://dev.to/unimatrix2/generator-functions-um-caso-de-uso-1ph0</link>
      <guid>https://dev.to/unimatrix2/generator-functions-um-caso-de-uso-1ph0</guid>
      <description>&lt;h2&gt;
  
  
  Contexto
&lt;/h2&gt;

&lt;p&gt;Oi! Antes de iniciar propriamente esse artigo, gostaria de apresentar o contexto de como cheguei a utilizar uma generator function para resolver um problema.&lt;/p&gt;

&lt;p&gt;Tudo começa alguns anos atrás quando pensei no meu projeto final do bootcamp que participei. A ideia inicial era um portal que agregava dados sobre políticos como notícias, projetos, votações e etc. Isso evoluiu para uma plataforma que utiliza os bancos eleitorais do TSE para monitorar políticos que estão exercendo cargos eletivos diretos. Esses bancos do TSE são distribuídos em formato CSV, mas não são perfeitos. Só nesses anos tentando evoluir esse projeto, os bancos melhoraram muito, e no meio do caminho o &lt;a href="https://brasilapi.com.br" rel="noopener noreferrer"&gt;Brasil API&lt;/a&gt; veio ao mundo também e me inspirou ainda mais.&lt;/p&gt;

&lt;p&gt;Mesmo que a stack do projeto já estivesse definida, estar constantemente atualizando meu banco principal com as constantes atualizações dos CSVs, corrigindo inconsistências e o tempo para se importar tudo se tornaram grandes empecilhos e percebi que precisaria de um programa só para lidar com isso. Começa a aventura da CLI que montei para resolver esse problema.&lt;/p&gt;

&lt;p&gt;Inicialmente, a ideia era simples: ler o CSV, lotear os dados e salvar os lotes no banco. Fui testando o tempo médio que levava para salvar tanto um a um, quanto vários de uma vez e qual tamanho de lote tinha o melhor tempo médio, mas nada muito criterioso ou exato (inclusive se alguém testar ou souber otimizações do tipo para MongoDB, aceito a crítica/sugestão). E funcionava legal, tinha agora um jeito rápido de passar o caminho do CSV para uma CLI que resolvia o meu problema.&lt;/p&gt;

&lt;p&gt;Já nem pensava mais nessa CLI, até que em uma viagem queria codar nesse projeto, meu notebook não tinha o banco e só 8GB de RAM, ou seja, era um editor de código e um sonho. Obviamente ele me deixou na mão porque o código era bem básico e subia todo o arquivo na memória antes de salvar no banco. Nessa altura, já havia lido sobre funções que "retornavam mais de uma vez" e a palavra reservada yield, pensei que era uma oportunidade interessante, fazia sentido pois a biblioteca de leitura de CSV que utilizei na CLI já possuia uma API para processar o CSV linha por linha.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema
&lt;/h2&gt;

&lt;p&gt;Com apenas 8GB de RAM, o desafio de otimização era refatorar a CLI para processar o arquivo linha por linha, em vez de carregar tudo na memória. Até então o fluxo era bem simples: carregar arquivo na memória, lotear e salvar os lotes no banco. Como repensar esse fluxo?&lt;/p&gt;

&lt;h2&gt;
  
  
  Pensando a solução
&lt;/h2&gt;

&lt;p&gt;O primeiro passo foi decidir o que manter. Bom, toda a estrutura da CLI vai ficar igual, o tamanho do lote vai ficar igual, operações de banco, vou mexer apenas na função que faz a leitura e como as operações de banco vão ocorrer quando mexer nessa parte. O segundo passo foi juntar curiosidade com aplicabilidade: como usar uma generator function para implementar a solução?&lt;/p&gt;

&lt;p&gt;Isso levou a uma série de considerações para escrever a solução:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O ideal é manter o loteamento de inserts no banco para garantir a performance dessa etapa, mas é necessário evitar a qualquer custo carregar mais dados na memória do que o essencial, então cada lote precisa ser preparado já no processamento do CSV&lt;/li&gt;
&lt;li&gt;Será necessário pensar bem onde bloquear e não bloquear o loop de eventos para garantir que nada está se perdendo ou lotes incompletos sejam processados sem comprometer a eficiência de memória e velocidade&lt;/li&gt;
&lt;li&gt;Será necessário usar async para juntar cada lote antes de entregar para o banco de dados salvar (para minha surpresa, existem  Async Generators também!)&lt;/li&gt;
&lt;li&gt;Cada lote que estiver pronto precisa ser entregue para o banco salvar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A cada consideração, ficou mais claro que uma Generator Function era exatamente o que eu precisava.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detalhe
&lt;/h3&gt;

&lt;p&gt;Pensar a solução não foi um processo muito custoso pois implementei na CLI uma estrutura parecida com as camadas de uma API convencional. Os comandos são como controllers e os serviços definem e executam o contrato. Em uma API existem mais camadas, mas como é uma CLI optei por não abstrair as operações de banco e usar a pasta &lt;code&gt;/lib&lt;/code&gt; para organizar funcionalidades que são utilizadas nos serviços. É nesses momentos que o seu eu de agora agradece o seu eu do passado pelo código organizado que permite alterações de todos os tamanhos com pouco esforço ou retrabalho.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementando a solução
&lt;/h2&gt;

&lt;p&gt;O primeiro passo aqui foi entender como e quais recursos de Async Generators vou utilizar. Os exemplos disponíveis na MDN já foram de grande ajuda para elaborar uma solução:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator" rel="noopener noreferrer"&gt;AsyncGenerator - JavaScript | MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function*" rel="noopener noreferrer"&gt;async function* - JavaScript | MDN&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Particularmente o último link foi muito útil para pensar como estaria chamando essa função para salvar os dados no banco, utilizando &lt;a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/for-await...of" rel="noopener noreferrer"&gt;um loop for...of assíncrono&lt;/a&gt;. Como essa parte da solução já era óbvia, implementei.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;mongoConnect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mongoUri&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;batch&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pathUri&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TseCandidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;Batch entries imported with success`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AppError&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error importing batch entries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;parseAndImportDb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;mongoDisconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Uma coisa já estava garantida portanto: cada lote de arquivos que ficar pronto é resolvido e imediatamente enviado para o banco. O resto do código serve basicamente para reportar no terminal o progresso ou eventuais erros das operações e garantir controle de fluxo.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Utilizando o &lt;code&gt;.then/.catch&lt;/code&gt; da promessa do banco, garanto que cada lote que for salvo com sucesso é independentemente reportado no terminal&lt;/li&gt;
&lt;li&gt;Utilizando o &lt;a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise/all" rel="noopener noreferrer"&gt;Promise.all&lt;/a&gt;, garanto que todas as operações de banco terminem antes de encerrar a conexão.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;É importante notar que o &lt;a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise/all" rel="noopener noreferrer"&gt;Promise.all&lt;/a&gt; joga um erro se qualquer uma das promessas falhar, então qualquer problema salvando um lote no banco compromete todo o resto da operação. Mas esse comportamento é desejado: preciso entender a razão do erro e corrigir o código ou entender uma inconsistência/corrupção de dados que possa estar ocorrendo e resolver isso. Como um banco incompleto é inútil e precisarei apagar tudo e começar novamente, não faz sentido correr o processo até o final, mas é importante conhecer o &lt;a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled" rel="noopener noreferrer"&gt;Promise.allSettled&lt;/a&gt; também!&lt;/p&gt;
&lt;h3&gt;
  
  
  Implementando o Async Generator
&lt;/h3&gt;

&lt;p&gt;Até então, a implementação era baseada em "embalar" a API da bilbioteca de leitura de CSV em uma promessa:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Papa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICandidate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;latin1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;skipEmptyLines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IParseResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;green&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; entries parsed`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resultsErrorsLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;
        &lt;span class="nx"&gt;resultsErrorsLength&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
          &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resultsErrorsLength&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;green&lt;/span&gt;
          &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;red&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; parsing errors`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AppError&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Errors were found on the table, import cancelled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;parse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ParserLib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AppError&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Parse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ParserLib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;})),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Essa implementação utilizava apenas a callback &lt;code&gt;complete&lt;/code&gt; da biblioteca, a ideia agora é utilizar a &lt;code&gt;step&lt;/code&gt;. Para quem estiver mais curioso sobre a biblioteca, chama-se &lt;a href="https://www.papaparse.com" rel="noopener noreferrer"&gt;Papaparse&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vamos enfim ao esboço dessa refatoração:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Note o asterisco caracterizando a função como um gerador assíncrono&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;AsyncGenerator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICandidate&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Temos aqui nosso tamanho de lote e array que acumulará as linhas em um lote&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;batchSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICandidate&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;// Temos aqui duas lets com uma promise e um resolver.&lt;/span&gt;
  &lt;span class="c1"&gt;// Note que a promise recebe o resolver externo como resolver&lt;/span&gt;
  &lt;span class="c1"&gt;// dela.&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICandidate&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICandidate&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;Papa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICandidate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Utilizei latin1 pois é a codificação dos bancos do TSE&lt;/span&gt;
    &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;latin1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;skipEmptyLines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;delimiter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// nossa lógica 'linha por linha' vai aqui&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Nossa lógica para finalizar o processamento vai aqui&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Aqui fica tudo mais interessante: utilizando de um&lt;/span&gt;
  &lt;span class="c1"&gt;// 'loop infinito', vamos travar o loop para aguardar a&lt;/span&gt;
  &lt;span class="c1"&gt;// promessa e se ele for um array (ou seja, um lote de dados)&lt;/span&gt;
  &lt;span class="c1"&gt;// rendemos ele ao loop do serviço para inserir no banco&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Fora a lógica principal de processamento, ainda é preciso lidar com a condição de parada do loop.&lt;/p&gt;

&lt;p&gt;É importante ressaltar como o fluxo dessa solução está estruturado: para cada lote de dados acumuluado, a ideia é utilizar o &lt;code&gt;resolver&lt;/code&gt; para resolver a promessa, "entregando" o lote de dados para o loop, que vai render (&lt;em&gt;yield&lt;/em&gt;) para outro loop. A utilização de lets tem uma dupla função:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;permitir a redesignação do lote e da promessa, garantindo que o array está vazio e o loop está aguardando a promessa resolver ou rejeitar&lt;/li&gt;
&lt;li&gt;dereferenciar o lote e a promessa antigos. Ao eliminar todas as referências ativas de um objeto, ele pode ser marcado para coleta de lixo, e ao usar redesignação, isso é realizado intencionalmente.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Um pouco mais a fundo - Coleta de lixo
&lt;/h4&gt;

&lt;p&gt;Vou deixar a introdução desse tópico para um bom artigo que encontrei sobre coleta de lixo no Node:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/_staticvoid" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F24722%2F502c728f-8a3a-4e5d-a038-e137d9fbe8bc.jpg" alt="_staticvoid"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/_staticvoid/node-js-por-baixo-dos-panos-9-coletando-o-lixo-4gh" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Node.js Por Baixo dos Panos #9: Coletando o Lixo&lt;/h2&gt;
      &lt;h3&gt;Lucas Santos ・ Apr 6 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#v8&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#computerscience&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;Um aspecto importante para se notar é que ao dereferenciar constantemente os dados/promessa antigos, poucas ou até nenhuma referência sobrevive a um ciclo de coleta no espaço novo, a memória é liberada o quanto antes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Processando as linhas
&lt;/h3&gt;

&lt;p&gt;Cada linha processada dispara a callback definida em &lt;code&gt;step&lt;/code&gt;. Basta então acessar o dado e acumular no lote. Por fim, quando o lote possuir o tamanho desejado, a promessa é resolvida com o lote e tanto o array quanto a promessa são redesignados.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;batchSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Processando o último lote e criando o sinal de parada
&lt;/h3&gt;

&lt;p&gt;Agora a callback de complete apenas resolve o último lote de linhas. Para a condição de parada, é criada mais uma promise que resolve com o objeto &lt;code&gt;{ done: true }&lt;/code&gt;. Isso será avaliado no loop da função para encerrar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Last batch parsed with success!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Parando o loop
&lt;/h3&gt;

&lt;p&gt;Para finalizar a implementação, basta validar no loop se há o sinal de parada e retornar. A implementação completa ficou assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;AsyncGenerator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICandidate&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;batchSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICandidate&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICandidate&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICandidate&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;Papa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICandidate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;latin1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;skipEmptyLines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;delimiter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;batchSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Last batch parsed with success!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Considerações finais
&lt;/h2&gt;

&lt;p&gt;Essa não é a solução que melhor utiliza os recursos de async generators, mas foi a solução que resolveu meu problema. Com ela, todo o processamento de dados foi otimizado e um trabalho que exigia um montante cada vez maior de memória agora exige um montante quase constante, permitindo até um sofrido notebook de 8GB de RAM montar um banco com milhares ou até milhões de linhas, em questão de minutos.&lt;/p&gt;

&lt;p&gt;Para complementar a funcionalidade e facilitar ainda mais, incluí um comando de Download na CLI, automatizando o trabalho de acessar e baixar os CSVs atualizados. Mas falta muita coisa. Ainda faltam funcionalidades de normalização de dados, descompactar seletivamente um CSV do arquivo fonte, um modo interativo e futuramente suporte a bancos relacionais.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/unimatrix2/tse-database-parser" rel="noopener noreferrer"&gt;O repositório&lt;/a&gt; está público, aberto a contribuições (embora a CLI ainda não esteja publicada, ainda falte um Código de Conduta e Regras de Contribuição) e possui uma licença &lt;a href="https://creativecommons.org/licenses/by-nc-nd/4.0/" rel="noopener noreferrer"&gt;CC BY-NC-ND 4.0&lt;/a&gt; pois acredito firmemente que o trabalhar com dados públicos exige zelo com a análise que se pretende realizar e com o público que venha a consumir esses dados, portanto, essa licensa impede derivativos diretos da CLI, para evitar que a ferramenta seja utilizada para (por exemplo) difundir desinformação ou análises enviezadas. A ideia é construir uma ferramenta de qualidade para uso geral.&lt;/p&gt;

&lt;p&gt;Peço perdão em adianto pela bagunça no código do repositório, nos momentos de luta implementei um logger da idade das pedras para persistir tudo em um arquivo de texto/JSON, mas é mais um dos itens que será bem trabalhado a seguir. Essa CLI ainda está meio longe de ser lançada.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Espero que esse artigo não seja tão maçante para o leitor e que ajude a esclarecer alguns recursos da língua ou ao menos sirva de inspiração para aventuras semelhantes de otimização. É minha primeira vez escrevendo um artigo do tipo, pretendo escrever outros e com cada vez mais qualidade.&lt;/p&gt;

&lt;p&gt;Muito obrigado pelo seu tempo e espero que tenha curtido. Até a próxima!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
      <category>performance</category>
    </item>
    <item>
      <title>Olá mundo!</title>
      <dc:creator>Danny</dc:creator>
      <pubDate>Mon, 15 Jul 2024 02:16:26 +0000</pubDate>
      <link>https://dev.to/unimatrix2/ola-mundo-40ge</link>
      <guid>https://dev.to/unimatrix2/ola-mundo-40ge</guid>
      <description>&lt;p&gt;Criei este perfil para compartilhar o que ando estudando, os projetos que estou desenvolvendo e as aventuras técnicas que resultam deles. Espero que os conteúdos que eu poste aqui sejam úteis ou ao menos informativos para quem os ler, e de repente ajude com algum problema similar que esteja enfrentando. Tentarei ser o mais acessível possível, é minha intenção fazer parte dessa rede/bolha de desenvolvedores brasileiros que se esforçam para produzir um conteúdo muito bom e que diversas vezes já me ajudou a aprender e resolver problemas, tanto em projetos pessoais quanto em produtos profissionais das firmas onde já trabalhei.&lt;/p&gt;

&lt;p&gt;Os posts serão escritos em pt-BR e pretendo abordar assuntos mais relacionados a projetos que estou construindo. Dessa forma, não vou produzir posts sobre assuntos mais básicos ou iniciantes das stacks/langs que uso ou vou usar, ao menos não inicialmente.&lt;/p&gt;

&lt;p&gt;Espero conseguir manter uma frequência semanal de posts, mas não sou de ferro e trabalho muito na semana. De qualquer maneira, uma calorosa recepção da pessoinha aqui do outro lado da tela! &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>coding</category>
    </item>
  </channel>
</rss>
