Quem vem acompanhando nosso canal no YouTube ou nossas redes sociais já deve ter nos ouvido tecendo altos elogios ao Deno. Isso tem um motivo: você precisa conhecer o Deno!
O framework é o sucessor do Node.js, já está disponível e digamos que virou nossas cabeças por aqui. Ryan Dahl, criador do Node.js, se juntou com outros desenvolvedores e elabarou seu sucessor com a missão de corrigir tudo que estava errado em seu projeto anterior. Considerando que Node.js é uma mão na roda, Deno tinha o potencial de ser um divisor de águas.
Deno pode ser descrito como um runtime TypeScript construído em V8, o motor de execução criado pelo Google para JavaScript. De cara, tem a vantagem de oferecer suporte nativo portanto tanto para JavaScript quanto para TypeScript. Além disso, Deno ainda ganha combinando o poder do Rust (para a criação de seu núcleo) e do Tokio (para lidar com loop de eventos).
Ryan Dahl juntou-se com Bert Belder e Bartek Iwańczuk para escrever um manifesto de intenções que ao mesmo explica o modelo adotado no framework, suas vantagens e seus objetivos, que traduzimos a seguir:
“Linguagens dinâmicas são ferramentas úteis. Scripts permitem que os usuários vinculem rápida e sucintamente sistemas complexos e expressem idéias sem se preocupar com detalhes como gerenciamento de memória ou construir sistemas. Nos últimos anos, linguagens de programação como Rust e Go tornaram muito mais fácil produzir código de máquina nativo sofisticado; esses projetos são um desenvolvimentos incrivelmente importantes na infraestrutura de computadores. No entanto, afirmamos que ainda é importante ter um ambiente de script poderoso que possa abordar uma ampla variedade de problemas.
JavaScript é a linguagem dinâmica mais amplamente usada, operando em todos os dispositivos com um navegador da web. Um grande número de programadores é fluente em JavaScript e muito esforço foi feito para otimizar sua execução. Através de organizações de padrões como a ECMA International, o idioma foi aprimorado de forma cuidadosa e contínua. Acreditamos que o JavaScript é a escolha natural para ferramentas de linguagem dinâmica; seja em um ambiente de navegador ou como processos independentes.
Nosso empreendimento original nessa área, Node.js, provou ser uma plataforma de software muito bem-sucedida. As pessoas acharam útil para a construção de ferramentas de desenvolvimento da Web, a criação de servidores da Web independentes e para uma infinidade de outros casos de uso. O Node, no entanto, foi projetado em 2009 quando o JavaScript era uma linguagem muito diferente. Por necessidade, Node teve que inventar conceitos que foram posteriormente adotados pelas organizações de padrões e adicionados à linguagem de maneira diferente. Na apresentação Design Mistakes in Node, isso é discutido em mais detalhes. Devido ao grande número de usuários que o Node possui, é difícil e lento evoluir o sistema.
Com a mudança da linguagem JavaScript e novas adições como o TypeScript, a criação de projetos do Node pode se tornar um esforço árduo, envolvendo o gerenciamento de sistemas de construção e outras ferramentas pesadas que tiram a graçados scripts de linguagem dinâmica. Além disso, o mecanismo de vinculação a bibliotecas externas é fundamentalmente centralizado através do repositório NPM, que não está alinhado com os ideais da web.
Consideramos que o cenário do JavaScript e a infraestrutura de software ao redor mudaram o suficiente para que valesse a pena simplificar. Buscamos um ambiente de script divertido e produtivo que possa ser usado para uma ampla variedade de tarefas.
Um navegador da Web para scripts de linha de comando
Deno é um novo runtime para executar JavaScript e TypeScript fora do navegador da web.
Deno tenta fornecer uma ferramenta independente para criar scripts rapidamente de funcionalidades complexas. Deno é (e sempre será) um único arquivo executável. Como um navegador da web, ele sabe como buscar código externo. No Deno, um único arquivo pode definir um comportamento arbitrariamente complexo sem nenhuma outra ferramenta.
import { serve } from "https://deno.land/[email protected]/http/server.ts"; for await (const req of serve({ port: 8000 })) { req.respond({ body: "Hello World\n" }); }
Aqui, um módulo completo do servidor HTTP é adicionado como uma dependência em uma única linha. Não há arquivos de configuração adicionais, não há instalação a ser feita previamente, basta deno run example.js.
Assim como em navegadores, o código é executado em uma sandbox segura por padrão. Os scripts não podem acessar o disco rígido, abrir conexões de rede ou executar outras ações potencialmente maliciosas sem permissão. O navegador fornece APIs para acessar câmeras e microfones, mas os usuários devem primeiro dar permissão. Deno fornece comportamento análogo no terminal. O exemplo acima falhará, a menos que o sinalizador da linha de comando allow-net seja fornecido.
Deno tem o cuidado de não se desviar das APIs JavaScript do navegador padronizadas. Obviamente, nem toda API de navegador é relevante para o Deno, mas onde eles estão, o Deno não se desvia do padrão.
Suporte TypeScript de primeira classe
Queremos que o Deno seja aplicável a uma ampla gama de domínios problemáticos: desde pequenos scripts de uma linha até lógica de negócios complexa do lado do servidor. À medida que os programas se tornam mais complexos, ter alguma forma de verificação de tipo se torna cada vez mais importante. O TypeScript é uma extensão da linguagem JavaScript que permite aos usuários fornecer opcionalmente informações de tipo.
Deno suporta TypeScript sem ferramentas adicionais. O runtime foi projetado com o TypeScript em mente. O comando deno types fornece declarações de tipo para tudo o que é fornecido pelo Deno. Os módulos padrão do Deno são todos escritos em TypeScript.
Promessas até o talo
Nod foi projetado antes do JavaScript ter o conceito de Promessas ou assíncrono/espera. A contrapartida de Node às promessas foi o EventEmitter, no qual se baseiam importantes APIs, como soquetes e HTTP. Deixando de lado os benefícios ergonômicos de assíncrono/espera, o padrão EventEmitter tem um problema de contrapressão. Pegue um soquete TCP, por exemplo. O soquete emitia eventos de “dados” quando recebia pacotes de entrada. Esses retornos de chamada de “dados” seriam emitidos de maneira irrestrita, inundando o processo com eventos. Como o Node continua recebendo novos eventos de dados, o soquete TCP subjacente não possui contrapressão adequada, o remetente remoto não faz ideia de que o servidor está em sobrecargae continua enviando dados. Para atenuar esse problema, um método pause() foi adicionado. Isso poderia resolver o problema, mas exigia código extra; e como o problema de inundação só se apresenta quando o processo está muito ocupado, muitos programas do Node podem ser inundados com dados. O resultado é um sistema com latência de cauda ruim.
No Deno, os soquetes ainda são assíncronos, mas o recebimento de novos dados exige que os usuários explicitamente façam read(). Nenhuma semântica de pausa extra é necessária para estruturar adequadamente um soquete de recebimento. Isso não é exclusivo para soquetes TCP. A camada de ligação de nível mais baixo do sistema está fundamentalmente ligada às promessas – chamamos essas ligações de “ops”. Todos os retornos de chamada em Deno, de uma forma ou de outra, surgem de promessas.
Rust tem sua própria abstração de promessa, chamada Futures. Através da abstração “op”, o Deno facilita a vinculação de APIs baseadas no future do Rust às promessas do JavaScript.
APIS de Rust
O principal componente que lançamos é a interface da linha de comandos (CLI) do Deno. A CLI é a versão 1.0 hoje. Mas o Deno não é um programa monolítico, e sim projetado como uma coleção de caixas de Rust para permitir a integração em diferentes camadas.
A caixa deno_core é uma versão muito simples do Deno. Não possui dependências no TypeScript nem no Tokio. Ele simplesmente fornece nossa infraestrutura de Op e Recursos. Ou seja, fornece uma maneira organizada de vincular os “futuros” do Rust às promessas de JavaScript. Naturalmente, a CLI é construída inteiramente sobre o deno_core.
A caixa rusty_v8 fornece ligações Rust de alta qualidade à API C ++ da V8. A API tenta corresponder a API C ++ original o mais próximo possível. É uma ligação de custo zero – os objetos expostos no Rust são exatamente o objeto que você manipula no C ++. (tentativas anteriores de ligações do Rust V8 forçaram o uso de identificadores persistentes, por exemplo). A caixa fornece binários criados no Actions CI do Github, mas também permite que os usuários compilem o V8 do zero e ajustem suas diversas configurações de compilação. Todo o código fonte da V8 é distribuído na própria caixa. Finalmente, o rusty_v8 tenta ser uma interface segura. Ainda não é 100% seguro, mas estamos chegando perto. Ser capaz de interagir com uma VM tão complexa quanto a V8 de uma maneira segura é incrível e nos permitiu descobrir muitos bugs difíceis no próprio Deno.
Limitações
É importante entender que o Deno não é um fork do Node – é uma implementação completamente nova. Deno está em desenvolvimento há apenas dois anos, enquanto Node está em desenvolvimento há mais de uma década. Dada a quantidade de interesse em Deno, esperamos que continue a evoluir e amadurecer.
Para algumas aplicações, o Deno pode ser uma boa escolha hoje, para outras ainda não. Depende dos requisitos. Queremos ser transparentes sobre essas limitações para ajudar as pessoas a tomar decisões informadas ao considerar usar o Deno.
Compatibilidade
Infelizmente, muitos usuários encontrarão uma frustrante falta de compatibilidade com as ferramentas JavaScript existentes. Deno não é compatível, em geral, com pacotes Node (NPM). Há uma camada de compatibilidade nascente sendo construída em https://deno.land/std/node/, mas está longe de estar completa.
Embora o Deno tenha adotado uma abordagem direta para simplificar o sistema de módulos, o Deno e o Node são sistemas bastante semelhantes, com objetivos semelhantes. Com o tempo, esperamos que o Deno seja capaz de executar mais e mais programas Node prontos para uso.
Performance de servidor HTTP
Acompanhamos continuamente o desempenho do servidor HTTP da Deno. Um servidor HTTP Deno “hello world” faz cerca de 25 mil solicitações por segundo com uma latência máxima de 1,3 milissegundos. Um programa Node comparável realiza 34 mil solicitações por segundo com uma latência máxima bastante irregular entre 2 e 300 milissegundos.
O servidor HTTP da Deno é implementado no TypeScript sobre os soquetes TCP nativos. O servidor HTTP de Node é escrito em C e exposto como ligações de alto nível ao JavaScript. Resistimos ao desejo de adicionar ligações nativas do servidor HTTP ao Deno, porque queremos otimizar a camada de soquete TCP e, mais geralmente, a interface operacional.
Deno é um servidor assíncrono adequado e solicitações de 25 mil por segundo são suficientes para a maioria dos propósitos (caso contrário, provavelmente o JavaScript não é a melhor opção). Além disso, esperamos que o Deno geralmente exiba melhor latência de cauda devido ao uso onipresente de promessas (discutido acima). Dito isso, acreditamos que há mais ganhos de desempenho no sistema e esperamos alcançar isso em versões futuras.
Gargalo TSC
Internamente, Deno usa o compilador TypeScript da Microsoft para verificar tipos e produzir JavaScript. Comparado com o tempo que a V8 leva para analisar o JavaScript, é muito lento. No início do projeto, esperávamos que os “Snapshots do V8” proporcionassem melhorias significativas aqui. Snapshots certamente ajudaram, mas ainda é insatisfatoriamente lento. Certamente acreditamos que existem melhorias que podem ser feitas aqui em cima do compilador TypeScript existente, mas é claro para nós que, em última análise, a verificação de tipo precisa ser implementada no Rust. Será um empreendimento enorme e não acontecerá tão cedo; mas forneceria melhorias de desempenho de ordem de magnitude em um caminho crítico experimentado pelos desenvolvedores. O TSC deve ser portado para Rust. Se você estiver interessado em colaborar com esse problema, entre em contato.
Plugins / Extensões
Temos um sistema de plugins nascente para estender o runtime do Deno com operações personalizadas. No entanto, essa interface ainda está em desenvolvimento e foi marcada como instável. Portanto, é difícil acessar sistemas nativos além daqueles fornecidos por Deno.”
Conclusão
O Deno tem pouco mais de dois anos de desenvolvimento e menos de dois meses de lançamento. Ainda está engatinhando. Entretanto, por tudo que testamos e avaliamos, é um framework com um grande futuro pela frente. Ele já está na sua versão 1.1.1, implementando mudanças e ajustes constantemente.
Não há momento melhor para começar a estudar o que ele pode oferecer. Já existe uma ampla documentação disponível online, assim como um manual completo. Acompanhe também o projeto no Github, Discord e Twitter.