Posts da Tag: NodeJS - Blog da TreinaWeb

Javascript

Conhecendo o MEAN Stack

De software livre e código aberto, o MEAN Stack é um conjunto de soluções JavaScript para a construção de páginas web dinâmicas e aplicações web que utilizam o JavaScript em todas as camadas da aplicação.

O termo MEAN foi utilizado pela primeira vez em 2013 pelo desenvolvedor Valeri Karpov e suas siglas significam: M – MongoDB, E – Express.js, A – Angular e N – Node.js.

Basicamente, o MEAN funciona da seguinte forma:

como funciona o MEAN stack?

O cliente utiliza a aplicação front-end desenvolvida em angular que envia a requisição feita pelo cliente para o node, que vai tratar a requisição e, através do express, acessa o banco de dados mongo para obter os dados a serem retornados.

A partir daí, o Express retorna os dados obtidos no banco para o node, que trata como a resposta da requisição será feita e, utilizando o angular, exibe os dados para o usuário.

Como vimos anteriormente, o MEAN utiliza as tecnologias Angular, Node.js, Express.js e MongoDB. Abaixo veremos um pouco sobre cada uma delas.

Ferramentas que compõe o MEAN

MongoDB

MongoDB

De código aberto, multiplataforma e lançado em 2009, o MongoDB é considerado um “líder” no quesito SGBD NoSQL.

É um banco de dados orientado a documentos, baseado no formato JSON (JavaScript Object Notation) e possui uma curva de aprendizagem baixíssima.

Dentre suas principais características, podemos citar:

  • Totalmente gratuito;
  • Possui uma baixa curva de aprendizagem, como dito acima;
  • Fácil escalabilidade horizontal;
  • Multiplataforma;
  • Suporte para transações ACID multi-documento;
  • Consultas suportam funções JavaScript personalizadas, entre outros.

É um SGBD muito utilizado tanto por pequenas quanto por grandes empresas, como mostra em seu site, como: Facebook, Ebay, Google, Adobe, entre outras.

O download do MongoDB pode ser feito em seu site, onde é possível encontrar toda sua documentação, suporte e muito mais.

MongoDB - Desenvolvedor
Curso de MongoDB - Desenvolvedor
CONHEÇA O CURSO

Express.js

ExpressJS

O Express.js por sua vez é um Framework para o desenvolvimento de aplicações JavaScript com o Node.js.

De código aberto, sobre a licença MIT, o Express.js foi desenvolvido para otimizar a construção de aplicações web e APIs, tornando-se um dos Frameworks mais populares da internet e que utiliza o Node para execução do javascript como linguagem de back-end.

Dentre suas principais características, podemos citar:

  • Possui um sistema de rotas completo;
  • Possibilita o tratamento de exceções dentro da aplicação;
  • Permite a integração de vários sistemas de templates que facilitam a criação de páginas web para suas aplicações;
  • Gerencia diferentes requisições HTTP com seus mais diversos verbos;
  • Feito para a criação rápida de aplicações utilizando um conjunto pequeno de arquivos e pastas;
Express - Otimização de aplicações Node.js
Curso de Express - Otimização de aplicações Node.js
CONHEÇA O CURSO

Angular

Angular

Angular é um framework para o desenvolvimento de aplicações web, que utiliza o JavaScript para desenvolvimento, tendo como principal objetivo o desenvolvimento de aplicações web mais robustas utilizando o JavaScript.

Foi criado por Misko Hevery e Adams Abrons em 2009, com um projeto inicial de que tinha como principal objetivo facilitar a criação de aplicações web.

O Angular foi construído sob o padrão MVVM ( Model-View, View-Model) abstraindo a camada de controle dos demais padrões conhecidos.

Dentre suas principais características, podemos citar:

  • É Open Source;
  • Mantido pelo Google;
  • Arquitetura da aplicação em camadas bem definidas;
  • Permite a criação modular e de componentes reutilizáveis;
  • Já possui a infraestrutura para integração com back-end;
  • Facilita a etapa de testes de forma automatizada.
Angular - Introdução
Curso de Angular - Introdução
CONHEÇA O CURSO

Node.js

NodeJS

O Node.js por sua vez, é uma plataforma open source que permite a execução de código JavaScript a nível front-end e back-end.

Em palavras mais simples, o Node.js é uma forma de executar o JavaScript do lado do servidor de uma aplicação.

Teve seu lançamento em 2009 sobre a licença MIT e é utilizado por diversas grandes empresas como Linkedln, Groupon, PayPal, entre outras.

O Node.js possui aplicabilidade em diversos meios, dentre eles podemos citar:

  • Criação de aplicações de chats e mensagens instantâneas;
  • Criação de API´s escaláveis;
  • Aplicações web que funcionam em real-time;
  • Aplicações CLI (Client Line Interface), entre outros.
Node.js Completo
Curso de Node.js Completo
CONHEÇA O CURSO

Podemos então concluir…

O uso do Javascript para criação de aplicações cresceu muito nos últimos anos. O que antes era apenas para o desenvolvimento front-end, hoje é utilizado para o desenvolvimento completo de diversos tipos de softwares. O MEAN é um ótimo exemplo deste poder do javascript, com ele conseguimos trabalhar com a linguagem em todas as camadas da aplicação.


Node

NPM – Você sabe o que dá para fazer com ele?

Olá Web Developers! Há um tempo falamos aqui sobre 10 truques do NPM. Mas você sabe o que dá para fazer com ele?

Gerenciamento de Dependências

O uso mais básico do NPM é o gerenciamento de dependências. Ou seja, você pode gerenciar o que deve ser baixado para seu projeto usar, como bibliotecas e frameworks. Todas as dependências ficam listadas no arquivo package.json.

Não só podemos baixar esses códigos do servidor do NPM, como também podemos baixar de outros lugares, como diretamente do Github.

Execução de Comandos

No package.json você pode definir uma lista de scripts. Isso é muito útil caso tenha algum script que você precise executar com frequência. Ferramentas como VS Code e WebStorm já interpretam a lista de scripts e disponibilizam para você executar com apenas um clique.

Mas não só podemos escrever pequenos scripts, como também podemos executar qualquer comando disponível em nossa máquina. Portanto, caso você tenha algum programa que pode ser executado pela linha de comando, mesmo que não seja JavaScript, poderá chamá-lo pelo NPM.

Automação de Tarefas

Caso você precise minificar arquivos, transpilar TypeScript e outras coisas, não precisa necessariamente configurar alguma ferramenta como Gulp ou WebPack. Basta baixar uma ferramenta que faça esse trabalho e chamá-la pelo NPM. Em certos casos a configuração da tarefa pelo NPM fica mais simples do que configurar essas outras ferramentas.

Npm - Gerenciador de pacotes para JavaScript
Curso de Npm - Gerenciador de pacotes para JavaScript
CONHEÇA O CURSO

Criação de Ferramentas

Você pode criar programas e instalá-los na máquina de forma bem simples. Já que podemos instalar os pacotes do NPM globalmente e depois chamá-los pelo terminal de qualquer lugar, isso significa que podemos criar nossas próprias ferramentas, que poderão ser instaladas e executadas de forma bem simples, com um simples comando.

Um exemplo de uma ferramenta nossa que usamos muito nos cursos é o Tw Dev Server, que inicia um servidor estático com Live Reload e também mantém navegadores sincronizados com cada ação que você fizer. Além disso, ele também simula um servidor com API e banco de dados.

Tw Dev Server - tela de desenvolvimento com navegadores sendo atualizados automaticamente

E não é apenas programas de linha de comando. Também podemos integrar com o Electron para criar aplicações Desktop. Por exemplo, o MemoPlay, programa que criei há algum tempo e que uso para gravar as aulas dos cursos aqui da TreinaWeb.

Memoplay Screen Recorder

E o que mais?

Bom, nessa pequena lista vimos o que dá para fazer com o npm. Porém, dentro de cada um desses itens há uma infinidade de possibilidades.

Já que foi dito que podemos instalar e executar qualquer programa, seja baseado em linha de comando ou possuir interface gráfica, isso já nos trás muitos poderes. Veja alguns exemplos mais usados com desenvolvimento de software:

  • Build de aplicações
  • Deploy de aplicações
  • Execução de Testes
  • Análise de qualidade de código
  • Automação de tarefas
  • Conversão de arquivos
  • Geração de arquivos e estrutura de projetos
  • Geração de certificados
Node.js Completo
Curso de Node.js Completo
CONHEÇA O CURSO

Javascript

O que é o Express.js?

Tendo sua versão inicial lançada no ano de 2010, o Express.js (ou somente Express) é um Framework para o desenvolvimento de aplicações JavaScript com o Node.js.

De código aberto, sobre a licença MIT, o Express.js foi desenvolvido para otimizar a construção de aplicações web e APIs, tornando-se um dos Frameworks mais populares da internet e que utiliza o Node para execução do javascript como linguagem de back-end.

Express - Otimização de aplicações Node.js
Curso de Express - Otimização de aplicações Node.js
CONHEÇA O CURSO

Relembrando Framework

Aqui no blog já possuímos um artigo que aborda “Para que serve um Framework”, mas em palavras mais simples, o framework é um facilitador no desenvolvimento de diversas aplicações e, sem dúvida, sua utilização poupa tempo e custos para quem utiliza, pois de forma mais básica, é um conjunto de bibliotecas utilizadas para criar uma base, onde as aplicações são construídas, um otimizador de recursos.

Possui como principal objetivo resolver problemas recorrentes com uma abordagem mais genérica. Ele permite ao desenvolvedor focar nos “problemas” da aplicação, não na arquitetura e configurações.

Um pouco sobre o Node.js

O Node.js por sua vez, é uma plataforma open source que permite a execução de código JavaScript a nível front-end e back-end.

Em palavras mais simples, o Node.js é uma forma de executar o JavaScript do lado do servidor de uma aplicação.

Teve seu lançamento em 2009 sobre a licença MIT e é utilizado por diversas grandes empresas como Linkedln, Groupon, PayPal, entre outras.

O Node.js possui aplicabilidade em diversos meios, dentre eles podemos citar:

  • Criação de aplicações de chats e mensagens instantâneas;
  • Criação de API´s escaláveis;
  • Aplicações web que funcionam em real-time;
  • Aplicações CLI (Client Line Interface), entre outros.

Possuímos aqui em nosso blog um artigo sobre Node.js, caso queira acompanhar.

Node.js Completo
Curso de Node.js Completo
CONHEÇA O CURSO

De volta ao Express.js

O Express.js é um Framework rápido e um dos mais utilizados em conjunto com o Node.js, facilitando no desenvolvimento de aplicações back-end e até, em conjunto com sistemas de templates, aplicações full-stack.

Escrito em JavaScript, o Express.js é utilizado por diversas empresas ao redor do mundo, dentre elas a Fox Sports, PayPal, IBM, Uber, entre outras.

Muito popular tanto em grandes empresas quanto na comunidade, o Express facilita a criação de aplicações utilizando o Node em conjunto com o JavaScript, tornando este ecossistema ainda mais poderoso.

Características do Express.js

O Express é um framework incrível e possui diversas características que facilitam o desenvolvimento de nossas aplicações. Dentre suas principais características, podemos citar:

  • Possui um sistema de rotas completo;
  • Possibilita o tratamento de exceções dentro da aplicação;
  • Permite a integração de vários sistemas de templates que facilitam a criação de páginas web para suas aplicações;
  • Gerencia diferentes requisições HTTP com seus mais diversos verbos;
  • Feito para a criação rápida de aplicações utilizando um conjunto pequeno de arquivos e pastas;

Podemos concluir que…

O Express.js é um ótimo Framework para o desenvolvimento de aplicações utilizando o Node. No site do Express.js podemos acessar toda a sua documentação, tutoriais, sua lista de recursos e muito mais.


Node

Instalação do Node.js – Windows, Mac e Linux

O Node.js é uma ferramenta que nos permite executar JavaScript fora do navegador, no lado do servidor. Ele foi um dos responsáveis por popularizar o JavaScript em diversas áreas. Podemos criar muito mais com JavaScript graças ao Node.js. Nesse post veremos como fazer a instalação do Node.js em Windows, Mac e Linux.

Acesse o site do Node, https://nodejs.org/en/. Assim que entrar no site, você verá dois botões, indicando duas versões para baixar (LTS e Current).

  • LTS é uma versão que tem um suporte mais prolongado, porém, costuma ser mais antiga. Ela é focada em estabilidade e segurança. Essa versão é mais recomendada para grandes projetos, que precisam de uma versão mais estável e que não podem ficar sendo atualizados.
  • Em contrapartida temos a Current, a versão mais atual com todas as novas funcionalidades, muito indicada para testes, estudos e novos projetos. Cada nova versão pode ter atualizações que podem quebrar um código que foi escrito em uma versão mais antiga. Portanto, é preciso tomar cuidado ao atualizar projetos para novas versões.

Com o propósito de estudos, recomendo sempre usar a Current. Desse modo você sempre terá as funcionalidades mais recentes.

Instalação em Windows

Logo após selecionar uma versão (LTS ou Current), será iniciado o download do instalador para Windows. Assim como é comum nos instaladores do Windows, basta seguir clicando nos botões Next até chegar ao final da instalação.

Assim que a instalação for finalizada, precisaremos testar se tudo está certo. Então, inicie o seu terminal. Pressione Tecla Windows + R, com a finalidade de abrir o programa Executar. Escreva powershell e aperte a tecla Enter.

Executar com o comando "powershell"

Em seguida, no terminal digite node -v e aperte a tecla Enter. Caso seja exibida a versão do Node, sua instalação foi feita com sucesso!

Powershell com comando "node --version"

Node.js Completo
Curso de Node.js Completo
CONHEÇA O CURSO

Instalação em Linux

Podemos instalar o Node.js facilmente com o próprio gerenciador de pacotes do Linux. Inicie o terminal pressionando Ctrl + Alt + T.

Em seguida, no terminal digite o comando:

curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -

Depois execute:

sudo apt-get install -y nodejs

Logo depois da instalação, no terminal digite node -v e aperte a tecla Enter. Se acaso a versão do Node for exibida, sua instalação foi feita com sucesso!

Instalação em Mac

Logo após selecionar uma versão (LTS ou Current), será iniciado o download do instalador para Mac (arquivo .pkg). Assim que abrir esse arquivo, o instalador será iniciado.

Instalador do Nodejs para Mac

Basta ir clicando para continuar até ele finalizar a instalação.

Precisamos testar se tudo está certo. Então, inicie o seu terminal. Para isso, pressione Command + Barra de Espaço. Digite terminal e pressione Enter.

Em seguida, no terminal digite node -v e aperte a tecla Enter. Caso seja exibida a versão do Node, sua instalação foi feita com sucesso!

Caso você precise ter mais de uma versão do Node.js em sua máquina, confira nosso post sobre como instalar várias versões do Node.js usando o NVM.

Express - Otimização de aplicações Node.js
Curso de Express - Otimização de aplicações Node.js
CONHEÇA O CURSO

Ferramentas

Tw Dev Server – como ele pode te ajudar a estudar programação?

Olá Web Developers! Vamos ver o que é e como utilizar o Tw Dev Server para te ajudar na hora de estudar programação.

O que é o Tw Dev Server?

Quando estamos trabalhando com desenvolvimento web precisamos de pelo menos um servidor de arquivos estáticos para nossos arquivos HTML, CSS, JavaScript, imagens, etc. Isso porque se abrirmos nosso HTML diretamente no navegador, certas operações não poderão ser feitas pelo JavaScript por questões de segurança.

Além disso, também é comum a necessidade de uma API para fazermos requisições, onde recuperamos ou salvamos dados quando estamos estudando desenvolvimento web ou criando um aplicativo mobile.

Para que essas necessidades básicas fossem resolvidas surgiu o Tw Dev Server. Ele veio com a finalidade de iniciar um servidor estático para nossos arquivos e também servir como um servidor que recebe requisições através de uma API. Com essa API podemos simular um servidor com banco de dados que recebe requisições para recuperar, salvar, modificar e apagar dados (CRUD). E tudo isso sem precisar de nenhuma configuração ou instalação complicada!

Tw Dev Server - tela de desenvolvimento com navegadores sendo atualizados automaticamente

Instalando o Tw Dev Server

O Tw Dev Server pode ser baixado pelo NPM ou como uma extensão do VS Code.

No NPM, para instalar globalmente em sua máquina, execute:

$ npm install -g @treinaweb/tw-dev-server

Assim que você tiver instalado, seu terminal terá o comando tw-dev-server disponível.

Iniciando um servidor de arquivos estáticos

Para iniciar um servidor de arquivos estáticos basta abrir o seu terminal na pasta raiz do seu projeto e executar:

$ tw-dev-server

Com isso nós já teremos nosso servidor rodando.

Tw Dev Server - Terminal

Assim como você pode ver na imagem acima, ele irá mostrar como você pode acessar seus arquivos pelo navegador. Na mesma máquina basta acessar http://localhost:3002. Entretanto, caso queira acessar de outra máquina ou dispositivo que estejam na mesma rede, basta acessar o endereço que é mostrado logo abaixo.

Como se pode notar, ele utiliza por padrão a porta 3002, mas isso pode ser alterado. Por exemplo, caso a gente queira utilizar a porta 4200:

$ tw-dev-server --port=4200

Para encerrar basta pressionar Ctrl + C.

JavaScript Básico
Curso de JavaScript Básico
CONHEÇA O CURSO

Live Reload e Browser Sync

Quando estamos escrevendo nosso HTML, CSS e JavaScript, pode ser meio cansativo ter que ficar atualizando o navegador a todo momento para ver o resultado do nosso trabalho.

Para isso o Tw Dev Server também possui a funcionalidade conhecida como Live Reload. Com essa função ativada, seu navegador vai atualizar automaticamente assim que você alterar algum arquivo HTML ou JavaScript. Caso a alteração seja em um arquivo CSS, o CSS da página é atualizado mesmo sem precisar atualizar o navegador.

Para ativar essa função, basta adicionar --live ao comando.

$ tw-dev-server --live

Além disso, também há a possibilidade de você querer testar sua página em vários navegadores, dispositivos ou tamanhos de janelas diferentes. E para facilitar isso temos a função Browser Sync, que vai sincronizar todos os navegadores que estiverem acessando a sua página.

Com isso você pode acessar a sua página a partir de vários navegadores, até mesmo do seu celular, e tudo o que você fizer em um navegador (como escrever, clicar num botão ou rolar a página) também acontecerá nos demais navegadores! Essa função também atualiza todos os navegadores quando você altera o seu código, não precisando ativar o --live.

Para ativar o Browser Sync basta adicionar --sync ao comando:

$ tw-dev-server --sync

Fazendo Requisições

Uma das principais funcionalidades do Tw Dev Server é disponibilizar uma API para podermos fazer requisições. E isso sem precisar configurar um back end ou um banco de dados. Porém, lembre-se que essa é uma ferramenta para facilitar nos estudos, portanto não deve ser usada para um produto que será lançado, pois não é otimizada para isso.

Podemos fazer operações GET, POST, PUT e DELETE. Para que possamos fazer requisições para a API, basta adicionar /api ao final do endereço que utilizamos para acessar os arquivos estáticos.
Em resumo, se você acessa os arquivos por http://localhost:3002, as requisições devem ser feitas a http://localhost:3002/api.

Logo após o /api nós adicionamos os nomes que quisermos. Isso será para nos organizarmos.

Painel Visual do Tw Dev Server

Para testar as requisições, ver seus dados, modificá-los, etc, você pode usar a ferramenta visual do Tw Dev Server. Basta acessar https://treinaweb.github.io/tw-dev-server/. Uma vez que o seu servidor estiver rodando, a bolinha ao lado do endereço aparecerá verde.

Tw Dev Server - Painel

Criando Dados

Imagine que a gente esteja num projeto de biblioteca e que queremos fazer uma operação POST para salvar um livro. Podemos, por JavaScript, fazer uma requisição com o seguinte código:

    let livro = {
        "nome": "GraphQL: A revolucionária linguagem de consulta e manipulação de dados para APIs",
        "autor": "Akira Hanashiro"
    };
    fetch('http://localhost:3002/api/biblioteca/livros', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(livro)
    })
    .then(response => response.json());
    .then(console.log)

Teremos como resposta um objeto com um campo id para identificar o dado recém cadastrado. Ele é gerado automaticamente pelo Tw Dev Server.

Não apenas por JavaScript, como também podemos fazer essa operação pela nossa ferramenta visual:

Tw Dev Server - Post

Buscando Dados

Para fazermos uma requisição GET e recuperar todos os livros, bastaria fazer:

    fetch('http://localhost:3002/api/biblioteca/livros')
        .then(response => response.json());
        .then(console.log)

E, claro, também podemos fazer isso na nossa ferramenta visual.

Porém, caso você queira que seja retornado apenas um único dado, pode-se passar o id do elemento. Por exemplo: http://localhost:3002/api/biblioteca/livros?id=1591291655803.

Atualizando Dados

Para atualizar um dado basta fazer uma operação PUT, usar a mesma URL e indicar nela o id do dado que você quer atualizar:

    let novoLivro = {...};
    fetch('http://localhost:3002/api/biblioteca/livros?id=1591291655803', {
        method: 'PUT',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(novoLivro)
    })
    .then(response => response.json());
    .then(console.log)

Assim que executar essa requisição teremos como resposta o dado atualizado.

Node.js Completo
Curso de Node.js Completo
CONHEÇA O CURSO

Apagando Dados

Por fim temos a função de apagar dados. Para isso basta fazer uma requisição DELETE passando o id do dado que se quer apagar.

    fetch('http://localhost:3002/api/biblioteca/livros?id=1591291655803', {
        method: 'DELETE',
    })
    .then(response => response.json());
    .then(console.log)

Caso você queira apagar todos os dados de uma coleção, basta passar all no lugar do id

    fetch('http://localhost:3002/api/biblioteca/livros?id=all', {
        method: 'DELETE',
    })
    .then(response => response.json());
    .then(console.log)

Em conclusão, o Tw Dev Server é uma ótima ferramenta que pode se aliar aos seus estudos em desenvolvimento web e mobile, seja servindo arquivos ou simulando um back end com api e banco de dados. Também pode ser usado para fazer testes caso você esteja desenvolvendo um front mas o back end ainda não tem uma api pronta para te retornar dados.


Node

Deno: conheça o suposto substituto do Node.js

Olá Web Developers! Acabou de ser lançada a versão 1.0 do Deno. Vamos conhecer esse suposto substituto do Node.js.

O que é Deno?

O Deno, assim como o Node.js, é um ambiente de execução de JavaScript. Ele foi feito pelo próprio criador do Node utilizando a linguagem Rust. Assim como o Node, Deno também utiliza o V8, engine de JavaScript do Google Chrome.

Por que outro interpretador de JavaScript?

Ryan Dahl, criador do Node, deu uma palestra em junho de 2018 na JSConf intitulada 10 Coisas que eu lamento pelo Node.js (10 Things I Regret About Node.js no original). Por mais que o Node tenha várias vantagens e seja muito usado, seu criador admitiu vários problemas.

Aliás, o Node.js foi criado em 2009, época em que o JavaScript nem tinha Promises ou async/await e nosso modo de trabalho, técnicas e boas práticas eram outras. O mundo todo era diferente. Lembre-se que sites eram feitos com tabelas, não usávamos smartphones, não havia YouTubers, etc.

Não só o Node.js possui problemas, como também várias linguagens, bibliotecas e frameworks. Todos nós, conforme vamos evoluindo na carreira de programador, aprendemos coisas novas e percebemos que algo que fizemos há algum tempo pode ser feito de maneira bem melhor. Além disso, a evolução de várias ferramentas e tecnologias também nos permite criar coisas melhores.

Isto é, com os aprendizados que Ryan Dahl teve em sua carreira, os desafios enfrentados com o Node e a evolução do JavaScript, ele pensou em criar algo novo. Logo após falar dos problemas do Node, Ryan apresentou seu novo trabalho, o Deno, que na época tinha apenas 1 mês de existência.

O Deno não apenas possui melhorias em coisas que foram consideradas ruins no Node, como o node_modules, como também possui várias novidades que o Node não tem e que são importantes.

Deno é mais seguro

Qualquer código no Node tem o poder de acessar coisas como sua rede e HD. Por outro lado, no Deno o seu código é executado em uma sandbox segura por padrão.

Ou seja, se você não der permissão, um código malicioso não poderá acessar suas informações. Isso se assemelha ao JavaScript nos navegadores, que apenas consegue acessar coisas como a câmera se o usuário der permissão.

Node.js Completo
Curso de Node.js Completo
CONHEÇA O CURSO

TypeScript Integrado

O Deno não interpreta apenas JavaScript. Ele também interpreta TypeScript sem que você precise instalar nem configurar nada.

Essa decisão foi baseada no crescimento do uso do TypeScript em grandes projetos que precisam lidar com complexas lógicas de negócio. Tudo no Deno foi feito com o TypeScript em mente.

Deno não precisa de NPM ou node_modules

Projetos criados com o Node utilizam o NPM para gerenciar suas dependências. Os pacotes baixados ficam guardados na pasta node_modules, que facilmente chega a tamanhos enormes.

Assim como navegadores, o Deno sabe como buscar códigos externos. Ou seja, podemos importar o código de um arquivo passando seu endereço. Veja abaixo um exemplo da criação de um simples servidor que utiliza um arquivo TypeScript externo.

import { serve } from "https://deno.land/std@0.50.0/http/server.ts";

for await (const req of serve({ port: 8000 })) {
  req.respond({ body: "Hello World\n" });
}

Promises em todo lugar

O JavaScript é famoso por sua natureza assíncrona. Porém, o Node foi criado antes do JavaScript possuir coisas como Promises e Async/Await.

Como resultado da evolução do JavaScript em união da linguagem Rust, o Deno tem um moderno sistema baseado em Promises. Isso nos permite trabalhar mais facilmente com código assícrono, além de evitar os problemas que o Node tinha em seu sistema.

JavaScript Avançado
Curso de JavaScript Avançado
CONHEÇA O CURSO

Deno é Compatível com Node?

Ainda que Node e Deno sejam interpretadores de JavaScript, o Deno não é totalmente compatível com os pacotes do seu irmão mais velho e outras ferramentas.

No entanto, uma camada de compatibilidade está sendo criada, mas ainda está longe de ser concluída.

Já posso trocar o Node.js pelo Deno?

A princípio, sim. O lançamento da versão 1.0 do Deno indica que ele já está em uma fase estável.

Ainda assim, lembre-se que o Deno está sendo desenvolvido há pouco mais de 2 anos. Ele não é um fork do Node, foi feito totalmente do zero. Por outro lado, o Node já possui mais de uma década de vida. Ou seja, o Node é mais maduro e possui uma comunidade bem maior.

Portanto, o mais indicado é sempre analisar as necessidades do seu projeto para poder escolher a tecnologia a ser usada.


Desenvolvimento

E-mails responsivos com a linguagem de marcação MJML

O MJML é um framework e linguagem de marcação para a criação de e-mails responsivos. Ele simplifica a escrita com uma linguagem simples e concisa (a Mailjet Markup Language) que é convertida para HTML.

Se você ainda não teve a oportunidade de ver o código HTML de um e-mail responsivo, costuma ser uma coisa muito bagunçada, pois os estilos são aplicados de forma inline e usa-se tabelas para a sua estruturação. Isso é feito para fazer com que o e-mail funcione no maior número possível de dispositivos.

HTML5 e CSS3 - Desenvolvimento web Básico
Curso de HTML5 e CSS3 - Desenvolvimento web Básico
CONHEÇA O CURSO

Para você ter ideia, esse trecho de MJML de um dos e-mails que enviamos na TreinaWeb:

<mj-section>
  <mj-column full-width="full-width">
    <mj-image src="https://d2knvm16wkt3ia.cloudfront.net/og/java-jax-ws-rs.png" href="https://www.treinaweb.com.br" />
    <mj-text align="center" font-size="35px" line-height="1.1"> <a href="https://www.treinaweb.com.br">Python - Collections</a> </mj-text>
    <mj-text align="justify"> Estruturas de dados são pontos cruciais em qualquer linguagem de programação. Sendo que em cada uma, podem ser implementadas de uma forma diferente. Por isso que conhecer as particularidades e recursos da linguagem em relação a estrutura de
      dados, é imprescindível a qualquer um que queira dominar uma linguagem. </mj-text>
    <mj-text align="justify"> Neste ponto o Python não fica atrás, fornecendo estruturas comuns em outras linguagens, bem como estruturas exclusivas. Neste curso veremos como manipulá-las e conheceremos os métodos de cada estrutura disponível nesta linguagem. </mj-text>
    <mj-button width="100%" background-color="#54CF8A" href="https://www.treinaweb.com.br">SAIBA MAIS SOBRE O CURSO</mj-button>
  </mj-column>
</mj-section>

É convertido para esse HTML:

<div style="Margin:0px auto;max-width:600px;">
    <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
        <tbody>
            <tr>
                <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;">
                    <div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
                        <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
                            <tr>
                                <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
                                    <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
                                        <tbody>
                                            <tr>
                                                <td style="width:550px;">
                                                    <a href="https://www.treinaweb.com.br" target="_blank" style="color: #444444;">
                                                        <img height="auto" src="https://d2knvm16wkt3ia.cloudfront.net/og/java-jax-ws-rs.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;" width="550">
                                                    </a>
                                                </td>
                                            </tr>
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                            <tr>
                                <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
                                    <div style="font-family:Helvetica, arial, sans-serif;font-size:35px;line-height:1.1;text-align:center;color:#444444;">
                                        <a href="https://www.treinaweb.com.br" style="color: #444444;">Python - Collections</a>
                                    </div>
                                </td>
                            </tr>
                            <tr>
                                <td align="justify" style="font-size:0px;padding:10px 25px;word-break:break-word;">
                                    <div style="font-family:Helvetica, arial, sans-serif;font-size:16px;line-height:24px;text-align:justify;color:#444444;"> Estruturas de dados são pontos cruciais em qualquer linguagem de programação. Sendo que em cada uma, podem ser implementadas de uma forma diferente. Por isso que conhecer as particularidades e recursos da linguagem em relação a estrutura de dados, é imprescindível a qualquer um que queira dominar uma linguagem. </div>
                                </td>
                            </tr>
                            <tr>
                                <td align="justify" style="font-size:0px;padding:10px 25px;word-break:break-word;">
                                    <div style="font-family:Helvetica, arial, sans-serif;font-size:16px;line-height:24px;text-align:justify;color:#444444;"> Neste ponto o Python não fica atrás, fornecendo estruturas comuns em outras linguagens, bem como estruturas exclusivas. Neste curso veremos como manipulá-las e conheceremos os métodos de cada estrutura disponível nesta linguagem. </div>
                                </td>
                            </tr>
                            <tr>
                                <td align="center" vertical-align="middle" style="font-size:0px;padding:10px 25px;word-break:break-word;">
                                    <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;width:100%;line-height:100%;">
                                        <tr>
                                            <td align="center" bgcolor="#54CF8A" role="presentation" style="border:none;border-radius:3px;cursor:auto;padding:10px 25px;background:#54CF8A;" valign="middle">
                                                <a href="https://www.treinaweb.com.br" style="background: #54CF8A; color: #ffffff; font-family: Helvetica, arial, sans-serif; font-size: 13px; font-weight: normal; line-height: 120%; Margin: 0; text-decoration: none; text-transform: none;" target="_blank">
              SAIBA MAIS SOBRE O CURSO
            </a>
                                            </td>
                                        </tr>
                                    </table>
                                </td>
                            </tr>
                        </table>
                    </div>
                </td>
            </tr>
        </tbody>
    </table>
</div>

Através desse exemplo já dá para você notar como o MJML ajuda na escrita e na clareza do que está sendo escrito.

E para escrever um e-mail usando MJML?

O intuito desse artigo não é o de masterizar o uso do MJML ou de te ensinar a passo a passo a sintaxe dessa linguagem de marcação. O MJML possui uma completa documentação que mostra passo a passo tudo o que pode ser desenvolvido com ele.

A ideia aqui é te dar o caminho da pedra, te apresentar os recursos disponíveis, o que você pode usar e te mostrar como converter o MJML para HTML em seu projeto.

A documentação:

A documentação do MJML não deve ser ignorada. Portanto, é um ótimo começo.

Try it live:

Você pode visualizar o resultado enquanto escreve no editor online disponível no site do MJML.

Templates:

O site oficial tem uma área com alguns templates para que você possa visualizar e tirar algumas ideias na hora de construir os seus e-mails.

A própria documentação fornece um template básico para você começar por ele, caso deseje.

E como vou converter o MJML para HTML?

O MJML precisa ser convertido para HTML para que então os seus e-mails sejam enviados. Há algumas formas de atingir esse objetivo. Vamos começar pelas manuais:

1) É possível converter MJML para HTML pelo editor online oficial.

2) É possível converter através da aplicação desktop disponível para Linux, macOS e Windows.

Agora, se dinamicamente/programaticamente você constrói o MJML e precisa convertê-lo “on the fly” antes dos envios, existem algumas opções:

1) Se registrar na API Oficial do MJML para que você receba uma credencial que te permitirá fazer uma requisição na API para converter MJML para HTML. É de grátis.

curl \ 
-X POST "https://api.mjml.io/v1/render" \ 
--user "APPLICATION-ID:SECRET-KEY" \ 
-d '{ 
"mjml":"<mjml><mj-body><mj-container><mj-section><mj-column><mj-text>Hello World</mj-text></mj-column></mj-section></mj-container></mj-body></mjml>" 
}'

2) O MJML é open-source e escrito em JavaScript. Ou seja, você próprio pode criar a sua API em NodeJS para a conversão do MJML para HTML. Veja no Github.

Como usamos o MJML na TreinaWeb

Na TreinaWeb usamos uma abordagem Serverless para essa conversão. Você não sabe o que é Serverless? Recomendo a leitura do artigo: Serverless: uma introdução.

Criamos uma função e fizemos o deploy dela na AWS Lambda, pois é na AWS onde administramos toda a nossa infraestrutura.

O código-fonte dessa função e do template do Serverless Framework para deploy na AWS, você encontra nesse repositório: KennedyTedesco/mjml-lambda.

Node.js Completo
Curso de Node.js Completo
CONHEÇA O CURSO

A função é extremamente simples, ela apenas invoca o código do MJML para realizar a conversão:

import mjml2html from 'mjml';

export async function convert(event, context, callback) {
  return mjml2html(event.mjml, {
      beautify: true,
      minify: true,
      keepComments: false,
      validationLevel: 'skip'
  });
}

Uma vez que a lambda está disponível para uso, a invocamos em um de nossos sitemas usando a SDK da AWS para PHP. Uma possível forma de atingir esse objetivo usando o PHP:

$lambda = new \Aws\Lambda\LambdaClient([
    'version' => 'latest',
    'region' => 'us-east-1',
]);

$result = $lambda->invoke([
    'FunctionName' => 'mjml-prod-to-html',
    'InvocationType' => 'RequestResponse',
    'LogType' => 'None',
    'Payload' => json_encode(['mjml' => '<mjml>...',]),
]);

$result = json_decode($result->get('Payload')->getContents(), true);

$html = $result['html']; // O HTML convertido

No entanto, se fosse necessário expor essa função na Web para que qualquer um utilizasse, poderíamos usar o API Gateway na “frente” da lambda.

Dessa forma conseguimos manter o template dos nossos e-mails bem fáceis de serem desenvolvidos e alterados, além de que trazemos para a nossa infraestrutura a responsabilidade de fazer a conversão, para não ter que depender de um serviço de terceiro (a API BETA do MJML).

Espero que o MJML ajude a sua equipe a produzir lindos e-mails responsivos. Até a próxima!

Desenvolvedor Front-end
Formação: Desenvolvedor Front-end
HTML, CSS e JavaScript são a base de toda a web. Tudo o que você está vendo aqui agora depende deste tripé. Nesta formação vamos iniciar aprendendo lógica. Em seguida veremos todos os aspectos do HTML, CSS e JavaScript. Por fim, aprenderemos Sass, Google Analytics, empacotar nossas aplicações com Webpack, criação de aplicações Desktop com Electron, UX/UI e uma introdução aos frameworks mais utilizados no mercado: Angular, React, Vue e Ember.
CONHEÇA A FORMAÇÃO

Node

Instalando e gerenciando várias versões do Node.js com NVM

Olá Web Developers!

Você já teve problemas ao atualizar o Node.js e algum projeto parar de funcionar?

Já aconteceu comigo várias vezes, mas normalmente era só reinstalar as dependências que tudo voltava a funcionar. Até que um dia eu tive que fazer uma alteração em um sistema em que eu não mexia há muito tempo.

Não deu problema no código do sistema, foi mais um problema de compatibilidade do Node.js com as versões mais antigas de algumas ferramentas que eram usadas naquele projeto, como o Gulp e Babel. Era muito mais rápido instalar uma versão mais antiga do Node.js do que arrumar a estrutura responsável pelo build da aplicação.

Mesmo que uma reinstalação dos pacotes fosse a solução, é desperdício de tempo ter que ficar reinstalando tudo em um projeto a cada vez que eu atualizo o Node, sendo que normalmente a alteração que eu tinha que fazer em um projeto antigo seria mudar só uma linha. Então percebi que com a necessidade de manter projetos mais antigos, era chegada a hora de manter mais de uma versão do Node.js instalado na minha máquina (que eu evitei por muitos anos).

Conheça o NVM!

Ficar gerenciando várias versões de algo pode ser chato, mas o NVM (Node Version Manager) deixa tudo bem simples. É um dos gerenciadores de versão de Node.js mais utilizados.

Com o NVM a gente pode ver as versões do Node, escolher quais queremos instalar ou desisntalar e definir qual queremos usar em cada momento ou projeto.

Ele funciona em MacOS e Linux. Caso você precise gerenciar no Windows existe um outro projeto chamado nvm-windows que também é muito bom (recomendado até mesmo por NPM, Google e Microsoft) e os comandos são iguais aos do NVM.

Instalação do NVM (MacOS e Linux)

É recomendado desinstalar qualquer versão do Node.js presente em sua máquina antes de instalar o NVM para evitar colisões.

Para instalar o NVM basta usar o curl ou Wget. Execute no terminal:

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash

ou

$ wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash

Isso vai executar um script que vai clonar o repositório do NVM e jogar em um diretório chamado ~/.nvm/, que é onde serão instaladas as várias versões do Node.js que quisermos.

A versão do NVM no momento de escrita deste post era a v0.34.0. Para garantir uma versão mais recente, copie esse mesmo comando na página do repositório do NVM no GitHub.

Node.js Completo
Curso de Node.js Completo
CONHEÇA O CURSO

Instalação no MacOS usando Homebrew

Caso queira instalar usando o Homebrew, basta executar o comando $ brew install nvm.

Depois precisamos criar um diretório para o NVM, lugar onde ele fará as instalações das várias versões do Node.js. Para isso basta executar o comando $ mkdir ~/.nvm/.

Por último é só configurar as variáveis de ambiente. Abra o arquivo .bash_profile com o comando vim ~/.bash_profile e cole o seguinte conteúdo

export NVM_DIR=~/.nvm
source $(brew --prefix nvm)/nvm.sh

E então basta executar source ~/.bash_profile.

Instalação do nvm-windows (Windows)

Como dito anteriormente, o NVM é só para MacOS e Linux. Você pode conseguir utilizá-lo no Windows usando o WSL (Windows Subsystem for Linux) ou pode usar um outro programa que é o nvm-windows.

A proposta do nvm-windows é funcionar igual ao NVM. Eles oferecem um instalador que você pode baixar pela lista de lançamentos do nvm-windows no repositório do GitHub.

Comandos do NVM (e do nvm-windows)

Caso você tenha acabado de instalar, pode ser necessário reiniciar o seu terminal ou sua máquina.

O nvm-windows precisa que o terminal seja iniciado em modo administrador e alguns comandos do nvm ainda não são completamente suportados.

Os comandos são bem simples.

Listar versões instaladas

Para ver as versões que estão instaladas em sua máquina:

$ nvm ls

Listar versões disponíveis para instalação

Este comando lista todas as versões disponíveis para baixar e instalar na sua máquina. Esse número de versão será usado no comando para realizar a instalação.

$ nvm ls-remote

Instalar uma versão

Para instalar usamos o comando install seguido pelo número da versão que queremos (mostrada no comando anterior, ls-remote).

$ nvm install vX.X.X

Basta mudar os X pelos números da versão que quer baixar como v.0.11.5 ou v.12.4.0.

Para instalar a versão mais recente, utilize node no lugar do número da versão:

$ nvm install node

A primeira versão que você instalar será usada por padrão sempre que você abrir o terminal. A versão padrão pode ser alterada depois.

Desinstalar uma versão

O comando uninstall é usado para desinstalar uma versão presente em nossa máquina. É utilizado da mesma maneira que o install

$ nvm uninstall vX.X.X

Usar uma versão do Node.js

Sempre que você abrir o terminal, a versão do Node.js usada é sempre a definida como padrão. Para usar outra versão instalada, execute o comando use seguido pela versão que você quer.

$ nvm use vX.X.X

Para usar a versão mais recente, digite node no lugar da versão:

$ nvm use node

Lembrando que ao abrir um novo terminal, a versão usada será novamente a definida como padrão.

Definir nome para uma versão

Para não ter que ficar chamando uma versão pelo seu número, podemos definir um tipo de apelido para cada versão. Para isso usamos o comando alias e passamos o nome do apelido e a versão que queremos apelidar.

$ nvm alias meunome vX.X.X

Com isso você poderá chamar a versão vX.X.X por meunome, como em:

$ nvm use meunome

Remover um nome de versão

Se você não quiser mais aquele apelido que você deu para uma versão, basta executar o comando unalias seguido pelo apelido que você quer esquecer.

$ nvm unalias meunome

Definir uma versão padrão

Para definir a versão que será usada sempre que você abrir o terminal, use o comando alias passando default como nome e em seguida a versão que você quer que seja a principal.

$ nvm alias default vX.X.X

Para que a versão padrão seja a versão instalada mais recente, basta escrever node no lugar do número da versão:

$ nvm alias default node

Indicação da versão atual

Para saber qual a versão atual do Node.js o terminal está usando, basta executar o comando current.

$ nvm current

Migração de pacotes globais

Quando alteramos a versão do Node.js que está sendo utilizada, a versão do NPM muda junto. Isso significa que se você utiliza algum pacote do NPM globalmente em uma versão, não terá acesso a ele quando estiver usando outra versão.

Para não ter o trabalho de instalar cada pacote global a cada nova instalação do Node.js, basta adicionar --reinstall-packages-from.

Com esse comando podemos, por exemplo, instalar a versão 6 do Node.js e já mandar ele automaticamente instalar os pacotes globais do NPM que instalamos quando estávamos usando a versão 5.

nvm install 6 --reinstall-packages-from=5

Como aprendemos até aqui, node é um atalho para indicar a versão mais recente. Caso queira instalar a versão mais recente disponível e já migrar os pacotes globais da versão mais recente que está instalada na sua máquina, basta executar:

nvm install node --reinstall-packages-from=node

No momento esta funcionalidade ainda não está disponível para o nvm-windows, sendo necessária a instalação manual dos pacotes globais sempre que instalar uma nova versão do Node.js.

Definição de versão por projeto

A intenção de usar o NVM é poder ter uma versão do Node.js para cada projeto, mas é muito difícil conseguir lembrar qual a versão foi usada em cada um.

Para isso, basta criar na raiz do projeto um arquivo com o nome .nvmrc e colocar dentro dele o número da versão do Node.js que está sendo utilizada nesse projeto, como:

v12.4.0

Com isso, ao abrir o terminal dentro do projeto e executar o comando nvm use, o NVM vai automaticamente encontrar o arquivo .nvmrc e utilizar a versão indicada.

Conclusão

Eu não costumava ligar para ter várias versões do Node.js em uma mesma máquina, pois sempre conseguia fazer funcionar em versões novas. Mas com o tempo pode acontecer de você ter que mexer em um código antigo e vai desperdiçar tempo arrumando para fazer funcionar caso esteja com uma versão nova.

Não só para linguagens de programação como Ruby e Python, mas outras ferramentas para desenvolvimento também como Unity, prefira sempre usar um gerenciador de versões, pois vai te economizar tempo e trabalho.


PHP

Promises no ReactPHP

No último artigo – Introdução à programação assíncrona em PHP usando o ReactPHP – tivemos uma introdução à programação assíncrona com PHP.

No artigo de hoje vamos comentar sobre Promises, que no ReactPHP trata-se de um componente. Promises no ReactPHP são uma implementação da API CommonJS Promises/A. Se você trabalha com JavaScript, vai notar muita similaridade com o que veremos a seguir.

Windows - Fundamentos para desenvolvedores
Curso de Windows - Fundamentos para desenvolvedores
CONHEÇA O CURSO

Afinal, o que é uma Promise?

Uma Promise é uma abstração que encapsula um resultado de uma execução assíncrona para ser utilizado quando ele estiver disponível. Promises desempenham a mesma tarefa que os Callbacks dos quais já estamos acostumados, só que de maneira bem mais elegante (principalmente quando lidamos com múltiplos callbacks de forma encadeada).

Dois conceitos importantes que temos que conhecer: Promise e Deferred. Já vimos que uma Promise representa o resultado de um código assíncrono, já Deferred representa a computação que vai gerar esse resultado em algum momento futuro. Um objeto Deferred sempre vai ter uma Promise associada que vai representar (encapsular) o resultado. Usar um objeto Deferred é uma forma de separar a Promise de quem vai resolvê-la ou rejeitá-la (em algum momento futuro).

Uma Promise possui três estados possíveis:

  • unfulfilled: É o estado inicial, pois o valor retornado de Deferred ainda é desconhecido.
  • fulfilled: Esse estado indica que a Promise está devidamente “alimentada” do resultado da computação de Deferred.
  • failed: Houve uma exceção durante a execução do Deferred (o executor pode ter chamado reject()).

Já um objeto Deferred possui dois métodos para mudar o estado da sua Promise:

  • resolve(string $result): Quando o código é executado com sucesso, esse método altera o estado da Promise para fulfilled (enviando para ela o resultado que ela encapsulára);
  • reject(string $reason): A execução do código falhou, altera o estado da Promise para failed.

Uma Promise também possui métodos para que definamos handlers (callbacks a serem executados) para as mudanças de estados dela, são esses: then(), done(), otherwise() e always().

Antes de iniciarmos, certifique-se de instalar o componente React Promise no seu projeto:

$ composer require react/promise

Vamos então ao primeiro exemplo do uso de um objeto Deferred e de sua Promise:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();
$promise = $deferred->promise();

$promise->done(function ($data) {
    echo 'Resultado: ' . $data;
});

$deferred->resolve('Olá mundo!'); // Resultado: Olá mundo!

Criamos um objeto Deferred e definimos um handler para o método done() da Promise associada a ele. Por fim, resolvemos a operação usando o método resolve(), método este que “altera” o estado da Promise (para fulfilled) e fez o nosso handler (a função anônima que definimos no primeiro argumento do método done()) ser executado.

No caso de o objeto Deferred explicitamente rejeitar uma operação alterando o estado da Promise para failed, podemos usar o segundo argumento do método done() para definir um handler a ser executado em caso de rejeição:

O método done() aceita como argumento handlers tanto para o estado fulfilled quanto para o failed:

public function done(callable $onFulfilled = null, callable $onRejected = null)

No primeiro argumento informamos o handler que será executado quando o estado da Promise mudar para fulfilled, no segundo argumento, um handler para ser executado quando o estado da Promise for alterado para failed.

Por exemplo, vamos informar um handler pro segundo argumento da Promise e então vamos executar o método reject() do Deferred:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();
$promise = $deferred->promise();

$promise->done(function ($data) {
    echo 'Resultado: ' . $data;
}, function($reason) {
    echo 'Motivo da falha: ' . $reason;
});

$deferred->reject('Erro interno'); // Motivo da falha: Erro interno

Portanto, temos a seguinte relação:

Diagrama promise

Promises podem ser encadeadas, ou seja, o valor de uma promise resolvida pode ser encaminhado para a próxima da sequência. Isso pode ser atingido usando os seguintes métodos: then() e otherwise(). O método then() é parecido com o done(), com a diferença que ele retorna uma nova promise, enquanto o done() sempre retorna null. E o método otherwise() é uma forma de definir um handler para quando o estado da Promise for failed (e ele retorna uma nova Promise).

Poderíamos, então, trocar o done() por then() no exemplo anterior:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();
$promise = $deferred->promise();

$promise->then(function ($data) {
    echo 'Resultado: ' . $data;
}, function($reason) {
    echo 'Motivo da falha: ' . $reason;
});

$deferred->reject('Erro interno'); // Motivo da falha: Erro interno

Ou, uma vez que then() retorna uma nova Promise, poderíamos usar o otherwise() de forma encadeada:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();
$promise = $deferred->promise();

$promise
    ->then(function ($data) {
        echo 'Resultado: ' . $data;
    })
    ->otherwise(function($reason) {
        echo 'Motivo da falha: ' . $reason;
    });

$deferred->reject('Erro interno'); // Motivo da falha: Erro interno

Outro exemplo com o encadeamento de Promises:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();

$deferred->promise()
    ->then(function ($data) {
        return "Olá, {$data}";
    })->then(function($data) {
        return "{$data}Web";
    })->then(function($data) {
        return strtoupper($data);
    })->then(function($data) {
        echo "{$data}!";
    });

$deferred->resolve('Treina'); // OLÁ, TREINAWEB!

Na programação assíncrona faz muito sentido uma Promise retornar outra Promise, pois não temos o resultado da operação de imediato (quando envolve I/O), o que temos é uma “promessa” de que em algum momento ele poderá vir a estar disponível. Nesse sentido, then() é um mecanismo para aplicar uma transformação em uma Promise e gerar uma nova Promise a partir dessa transformação.

Slim - Microframework PHP
Curso de Slim - Microframework PHP
CONHEÇA O CURSO

Outro exemplo de encadeamento de then() com otherwise():

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();

$deferred->promise()
    ->then(function ($data) {
        return "Olá, {$data}";
    })->then(function($data) {
        throw new InvalidArgumentException("{$data}Web");
    })->otherwise(function(InvalidArgumentException $exception) {
        return strtoupper($exception->getMessage());
    })->done(function($data) {
        echo "{$data}!";
    });

$deferred->resolve('Treina'); // OLÁ, TREINAWEB!

Nesse exemplo o nosso handler em otherwise() só vai ser invocado se ele receber uma exceção do tipo InvalidArgumentException (e fizemos ele receber).

Outro exemplo:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();

$deferred->promise()
    ->then(function ($data) {
        return "Olá, {$data}";
    })->then(function($data) {
        throw new RuntimeException("{$data}Web");
    })->otherwise(function(InvalidArgumentException $exception) {
        return strtoupper($exception->getMessage());
    })->otherwise(function(Exception $exception) {
        return strtolower($exception->getMessage());
    })->done(function($data) {
        echo "{$data}!";
    });

$deferred->resolve('Treina'); // olá, treinaweb!

Nesse exemplo, como a promise lança uma exceção do tipo RuntimeException, apenas o handler que está esperando por uma exceção genérica (do tipo Exception) será invocado:

...
})->otherwise(function(Exception $exception) {
  return strtolower($exception->getMessage());
})
...

E, claro, se nenhuma promise passar pelo estado failed os nossos handlers que lidam com as falhas não serão invocados:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();

$deferred->promise()
    ->then(function ($data) {
        return "Olá, {$data}";
    })->then(function($data) {
        // throw new RuntimeException("{$data}Web");
        return "{$data}Web";
    })->otherwise(function(InvalidArgumentException $exception) {
        return strtoupper($exception->getMessage());
    })->otherwise(function(Exception $exception) {
        return strtolower($exception->getMessage());
    })->done(function($data) {
        echo "{$data}!";
    });

$deferred->resolve('Treina'); // Olá, TreinaWeb!

Agora que já entendemos o funcionamento do objeto Deferred e como as Promises funcionam, podemos verificar como seria resolver (ou rejeitar) uma Promise sem um objeto Deferred:

<?php

require './vendor/autoload.php';

use React\Promise\Promise;

$promise = new Promise(function(Closure $resolve, Closure $reject) {
    if (\random_int(1, 1000000) % 2 === 0) {
        $resolve('Gerou um número par.');
    } else {
        $reject('Gerou um número ímpar.');
    }
});

$promise->then(function($data) {
    echo 'Sucesso: ' . $data;
})->otherwise(function($reason) {
    echo 'Falha: ' . $reason;
});

Nesse exemplo instanciamos um objeto Promise passando para ele uma função anônima que vai cuidar da computação que precisamos realizar. Apenas para fins didáticos, estamos gerando um número inteiro aleatório e então verificamos se ele é par, se verdadeiro, resolvemos a Promise, caso contrário, a rejeitamos. Nisso que passamos a função anônima no construtor da classe Promise, ela é invocada e recebe duas novas funções anônimas como argumento: $resolve e $reject. São essas funções que invocamos no término da nossa operação para decidir o estado da Promise, se vai ser fulfilled ou failed. Por fim, apenas definimos handlers que serão executados em caso de sucesso ou falha e, para isso, usamos os métodos then() e otherwise().

Observe que a diferença dessa abordagem (de instanciar um objeto Promise diretamente) para a que usa um objeto Deferred, é que nessa última invertemos o controle de quem resolve a Promise, ou seja, essa responsabilidade fica com o objeto Deferred.

Agora que já vimos o essencial do funcionamento das promises, podemos fazer um paralelo de como seria um código que usa callbacks versus um que usa promises (usando JavaScript como linguagem de referência):

PHP Avançado
Curso de PHP Avançado
CONHEÇA O CURSO
request('http://www.treinaweb.com.br', function (error, response) {
    if (error) {
        // Aqui a gente lida com o erro.
    } else {
        request('http://www.treinaweb.com.br/' + response.path, function (error, response) {
            if (serror) {
                // Aqui a gente lida com o erro.
            } else {
                // Aqui lidaríamos com o sucesso da requisição.
            }
        });
    }
});

Nesse modelo estamos sempre aninhando callbacks uns dentro dos outros, o que nos torna suscetíveis ao mal do callback hell. O mesmo exemplo acima usando a biblioteca axios que trabalha com Promises ficaria assim:

axios.get('http://www.treinaweb.com.br')
    .then(function (response) {
        return axios.get('http://www.treinaweb.com.br/' + response.path);
    })
    .then(function (response) {
        // Lida com a resposta da requisição anterior
    })
    .catch(function (error) {
        // Lida com alguma exceção, se existir.
    });

No artigo de introdução ao ReactPHP mostramos um exemplo que usava a library reactphp-buzz. Veremos outro exemplo com ela, pois ela faz uso intensivo de promises. Primeiro instale-a como dependência do projeto:

$ composer require clue/buzz-react:^2.6

O nosso exemplo vai de forma assíncrona imprimir o bairro de alguns CEPs usando a API pública do Postmon:

<?php

require './vendor/autoload.php';

use Clue\React\Buzz\Browser;
use React\EventLoop\Factory;
use Psr\Http\Message\ResponseInterface;

$browser = new Browser(
    $loop = Factory::create()
);

$ceps = [
    '01311200', // Bela Vista
    '70630904', // Setor Militar Urbano
    '70165900', // Zona Cívico-Administrativa
    '32685888', // Erro, cep não existe.
];

foreach ($ceps as $cep) {
    $browser->get("https://api.postmon.com.br/v1/cep/{$cep}")
        ->then(function (ResponseInterface $response) {
            $endereco = \json_decode($response->getBody());

            echo $endereco->bairro . PHP_EOL;
        })
        ->otherwise(function (\Exception $exception) use ($cep) {
            echo 'Erro no CEP: ' . $cep . PHP_EOL;
        });
}

$loop->run();

Esse exemplo demonstra que o método get() que realiza uma requisição HTTP retorna uma Promise. Se você executar esse exemplo várias vezes, verá que não terá uma ordem definida para a impressão dos resultados, ademais, um resultado só é impresso quando ele fica pronto, não seguindo uma ordem linear (que é o que estamos acostumados na programação síncrona).

Para finalizar, é importante pontuar que Promises por si só não fazem nossos códigos serem assíncronos, elas são apenas mecanismos para encapsular os resultados.

Até a próxima!

Desenvolvedor PHP
Formação: Desenvolvedor PHP
Nesta formação você aprenderá todos os conceitos da linguagem PHP, uma das mais utilizadas no mercado. Desde de conceitos de base, até características mais avançadas, como orientação a objetos, práticas de mercado, integração com banco de dados. Ao final, você terá conhecimento para desenvolver aplicações PHP usando as práticas mais modernas do mercado.
CONHEÇA A FORMAÇÃO

PHP

Introdução à programação assíncrona em PHP usando o ReactPHP

Antes de entrarmos no comparativo do modelo síncrono versus assíncrono, veremos uma introdução, o essencial, sobre como uma requisição funciona em uma aplicação PHP tradicional.

A maior parte das aplicações escritas em PHP funcionam no clássico modelo de requisição e resposta de curto tempo de vida. Uma requisição é feita, o código é interpretado e depois compilado, a execução é realizada, dados são retornados e tudo é descarregado da memória na sequência, tudo acontece de forma isolada, sem compartilhar contexto. De forma simplificada, esse é o ciclo de vida da execução de um script no PHP a cada nova requisição feita.

Você pode estar imaginando que isso é muito custoso, ter sempre que passar pela interpretação e compilação a cada nova requisição. Você está certo. Mas o PHP implementa mecanismos que otimizam esse processo, para não ter que interpretar e compilar o código o tempo todo. O PHP interpreta os códigos e os compila (de forma implícita, ou seja, quando ele julga necessário) para bytecodes (uma versão intermediária de código) e coloca isso em memória compartilhada quando ele percebe que aquela parte é muito requisitada/utilizada (tarefa da extensão nativa OPCache). Além disso, o PHP implementa outros mecanismos (de mais baixo nível) de otimização da execução desse código intermediário.

De qualquer forma, mesmo com os mecanismos de otimização, a essência do modelo de requisição e resposta se mantém a mesma. O diagrama abaixo exemplifica como funciona esse ciclo de execução:

Diagrama de execução

Esse diagrama encurtou propositalmente uma etapa, a que passa pelo PHP-FPM, um gerenciador de processos, muito usado junto ao Nginx (servidor web), pois poderíamos ter um artigo só para falar sobre ele. A ideia aqui é entender o básico de como a requisição passa pelo servidor web e depois é retornada para o cliente. O PHP-FPM dispõe de pools de processos, ele cria, controla e encerra, de acordo com a demanda (o que o Nginx está encaminhando pra ele) e capacidade do hardware para tal (memória, principalmente).

Desenvolvedor PHP
Formação: Desenvolvedor PHP
Nesta formação você aprenderá todos os conceitos da linguagem PHP, uma das mais utilizadas no mercado. Desde de conceitos de base, até características mais avançadas, como orientação a objetos, práticas de mercado, integração com banco de dados. Ao final, você terá conhecimento para desenvolver aplicações PHP usando as práticas mais modernas do mercado.
CONHEÇA A FORMAÇÃO

Modelo síncrono

Num ambiente síncrono (tradicional) as instruções (partes) do programa são executadas uma por uma e apenas uma por vez, de forma sequencial:

<?php

echo "Hello ";

sleep(4); // Espera 4 segundos

echo "World";

Esse script vai demorar 4 segundos para ser executado e finalizado. A execução acontece linha por linha, de forma bloqueante. Se uma instrução precisa aguardar algum tempo (seja para ler algo do disco ou fazer alguma operação na rede), isso terá de ser concluído para que a próxima instrução seja executada e até mesmo para que ela use os dados previamente recuperados/preparados. Ou seja, parte-se da premissa de que a instrução anterior precisa ter sido concluída com sucesso (sem erros) para que uma nova seja executada (dependência).

Diagrama de instruções

Esse modelo funciona muito bem pra operações que usam mais CPU que I/O, pois a resolução de uma operação na CPU é muito mais eficiente do que uma operação que envolva I/O (uma requisição na rede, a leitura de um arquivo, esse tipo de operação é de alta latência).

Só que pense no seguinte problema: você decidiu implementar uma tarefa que precisa verificar se os links de um site estão todos online. No modelo síncrono, teríamos que partir da primeira requisição, aguardar o resultado dela (momento de ociosidade do programa) e então partir para próxima seguindo o mesmo fluxo até a última (sempre de forma sequencial e uma só iniciando após a finalização da outra).

Mas, não seria melhor ao invés de esperarmos a primeira requisição ser finalizada já inicializarmos as outras requisições e depois de um tempo voltar para pegar os resultados produzidos por elas? Pois bem, essa é a ideia central do modelo assíncrono, ele minimiza a ociosidade do programa alternando entre as tarefas. Num código assíncrono as tarefas são intercaladas sem precisar envolver novas threads, ou seja, de forma single-thread (como funciona o PHP e NodeJS, por exemplo).

Modelo assíncrono

Um código assíncrono lida com dependências e ordem de execução de eventos, ou seja, lida basicamente com tempo. É comum associar assincronismo com paralelismo, pois o assincronismo dá essa sensação que muita coisa está sendo executada no mesmo instante de tempo, no entanto, ao invés disso, no assincronismo muita coisa é feita ao mesmo tempo (concorrentemente) só que uma coisa por vez, nunca no mesmo instante de tempo (o fluxo de execução alterna entre as tarefas). Não existe paralelismo num código assíncrono, ou seja, um código assíncrono não tem suas tarefas distribuídas em múltiplas unidades de processamento, igual comentamos anteriormente, é single-thread (apesar de ser possível atingir paralelismo com assincronismo num ambiente multi-thread, mas foge do escopo do nosso artigo e normalmente necessita de algum caso de uso bem específico, devido às dificuldades técnicas de se implementar e sincronizar a comunicação).

Um código assíncrono continua executando uma tarefa por vez, ele apenas não fica preso em ociosidade enquanto uma tarefa ainda está aguardando algum resultado de I/O, por exemplo. Ao invés de ficar “bloqueado” aguardando, ele alterna de tarefa, inicia outros trabalhos e volta nas outras tarefas em um tempo futuro quando elas estiverem prontas. Fazendo uma analogia, vamos supor que você tem uma tarefa que precisa fazer duas requisições na internet, o seu código assíncrono vai lidar dessa forma:

“Faça essa primeira requisição, mas não vou ficar aqui esperando o resultado, me avise quando tudo estiver pronto. Enquanto isso, deixa eu executar a segunda requisição aqui.”

Enquanto no código síncrono seria:

“Faça essa primeira requisição. Eu terei que ficar esperando essa resposta, pois necessito dela para continuar o meu fluxo de trabalho. [ … algum tempo depois …] Obrigado pela resposta, agora, por gentileza, execute essa segunda requisição? Ficarei aqui aguardando o resultado dela. [… algum tempo depois …] Obrigado pela resposta. Agora posso concluir meu trabalho.”

Se você desenvolve um código síncrono para resolver uma operação matemática e porta esse código para um modelo assíncrono, você vai notar que ambos serão executados praticamente no mesmo tempo, sem nenhum levar vantagem sobre o outro. Agora, a história muda completamente se o seu problema precisa realizar alguma operação I/O (que naturalmente é bloqueante) ou quando ele precisa aguardar algum tempo por alguma coisa, nesse tipo de caso, o modelo assíncrono leva muita vantagem, como mostra esse diagrama:

diagramas de tarefas assincronas

Veja que nesse diagrama as tarefas alternam entre si, o modelo assíncrono tenta sempre evitar ociosidade/espera/bloqueio. Ele só fica bloqueado/aguardando quando nenhuma tarefa pode fazer nenhum progresso, aí ele precisa receber alguma chamada para voltar à sua operação.

A abordagem assíncrona não é a solução para todos os problemas, mas em comparação com o modelo síncrono, ela performa melhor principalmente nos seguintes cenários:

  • Quando o programa contém tarefas que fazem uso intensivo de I/O;
  • Quando o programa contém tarefas independentes, ou seja, quando umas não precisam esperar pelas outras para realizar seus trabalhos (e essas passam por algum estado de progresso em suas atividades).

Quando não faz tanto sentido:

  • Uma aplicação que faz um uso intensivo da CPU em que as operações são dependentes, ou seja, uma precisa ser finalizada para que a outra entre em cena;
  • Uma aplicação que realiza grandes operações de I/O mas que o uso da aplicação em si é infrequente e não há necessidade de escalar;

Levando para exemplos do “mundo real” você vai ver com frequência o uso de programação assíncrona para:

  • Uma API em que o usuário faz uma requisição e precisa de uma resposta rápida sem que precise esperar alguma operação ser finalizada (essa operação pode continuar rodando lá no servidor enquanto o usuário já obteve a resposta dele). Nesse sentido, a interface do usuário não fica congelada esperando uma resposta de uma operação que ele não precisa esperar por ela.
  • Data Streaming (dá pra construir, por exemplo, até um servidor de streaming de vídeo);
  • Aplicação de monitoramento;
  • Criação de chats;
  • Etc;

Apesar de termos dado exemplos clássicos aqui, é perfeitamente possível integrar código assíncrono numa aplicação tradicional (de abordagem síncrona) se você perceber que em determinado momento requisições externas precisam ser feitas ou alguma operação importante que envolva I/O, você pode estudar a possibilidade de implementar um código assíncrono nessa parte para obter o benefício da não ociosidade e melhorar o tempo de resposta do seu usuário. Não existem “regras estritas” aqui, você vai precisar avaliar caso a caso e decidir o que achar melhor. Mas, certamente, é mais comum ver scripts que rodam em linha de comando (CLI) utilizarem a abordagem assíncrona.

PHP Assíncrono

O PHP não dispõe (mas há a intenção de se implementar isso em algum momento da versão 8 do PHP) de mecanismos nativos para lidar com código assíncrono, diferente do JavaScript e C#, por exemplo. Por isso bibliotecas como Amp e ReactPHP se tornaram relevantes, pois elas abstraem isso. Nesse artigo introdutório, usaremos os componentes do ReactPHP.

O ReactPHP é baseado no padrão Reactor (o mesmo usado pelo NodeJS) que é uma implementação de uma arquitetura orientada a eventos (event-driven). A ideia é permitir que iniciemos múltiplas operações I/O sem que precisemos esperar pela finalização delas (não bloqueante). Ao invés disso, somos notificados quando algo importante acontecer e reagimos a esse evento com um callback (se você programa em JavaScript certamente já está familiarizado com isso).

O ReactPHP possui uma série de componentes independentes e o principal deles, que é o seu core é o EventLoop, ele é a base para o funcionamento de todos os outros componentes que o ReactPHP disponibiliza. O componente EventLoop é uma implementação padrão Reactor.

O Event Loop é basicamente um while infinito que faz o papel de ser o Scheduler das operações. Ele sequencialmente processa a fila de eventos e cuida da execução dos callbacks. Ele é o único código sendo executado sincronamente, nenhum outro código é executado em paralelo. E, como já dissemos anteriormente, ele roda em uma única thread. O fato do seu processador ter 16 núcleos ou 1, em nada vai interferir, a execução do Event Loop continuará sendo single-thread. A ideia por trás do ReactPHP é fazer um bom uso do tempo da CPU (sem cair na ociosidade com as operações de I/O) e não exatamente em paralelizar processos (o que demandaria diversos outros problemas de comunicação, troca de estados, trocas de contexto por parte do sistema operacional além, claro, de recursos de hardware).

O funcionamento é mais ou menos assim:

  • Você registra um evento;
  • Você passa a “ouví-lo” (listening);
  • Quando esse evento é disparado, você reage a ele via um handler e executa algum código.

Diagrama simplificado de funcionamento de um Event Loop:

ReactPHP

O ReactPHP possui quatro implementações possíveis de Event Loop e ele por padrão escolhe qual usar a partir da análise das extensões instaladas no seu ambiente PHP.

As implementações são:

  • StreamSelectLoop – Essa implementação funciona nativamente no PHP sem precisar de nenhuma extensão específica, ela executa chamadas de sistema select que resolvem a implementação do event loop, mesmo que não na performance das opções que serão mostradas abaixo.
  • LibEventLoop – Essa opção usa a extensão libevent do repositório pecl.
  • LibEvLoop – Usa a extensão libev. Funciona de forma similar à libevent citada acima.
  • ExtEventLoop – Usa a extensão event. Funciona de forma similar à libevent citada anteriormente. Essa é a minha extensão de escolha, por ser a mais atualizada e um dos desenvolvedores dela também trabalha no core do PHP. Você pode ver mais detalhes sobre ela clicando aqui.

Mas, calma lá! Não é escopo nosso, por enquanto, se preocupar com tudo isso. Se você vai desenvolver uma aplicação para produção que vai usar ReactPHP, ótimo, eu lhe recomendaria muitíssimamente instalar a extensão event. Mas, para o nosso objetivo didático, vamos deixar que o próprio ReactPHP escolha a melhor implementação pra gente, baseando-se no que temos instalado em nosso ambiente. Se não tivermos nenhuma das três últimas extensões, ele vai usar a primeira implementação, a **StreamSelectLoop **(standalone).

Ele possui uma factory que decide qual das implementações acima será usada:

$loop = ReactEventLoopFactory::create();

As soluções escritas usando o ReactPHP não dependem de nada específico de nenhuma das implementações acima, ou seja, não importa qual a implementação será usada, o ReactPHP se comportará da mesma maneira, as interfaces são as mesmas para todas. Isso nos dá a liberdade de não nos preocuparmos em instalar uma extensão para desenvolvermos alguns testes.

Desenvolvedor PHP
Formação: Desenvolvedor PHP
Nesta formação você aprenderá todos os conceitos da linguagem PHP, uma das mais utilizadas no mercado. Desde de conceitos de base, até características mais avançadas, como orientação a objetos, práticas de mercado, integração com banco de dados. Ao final, você terá conhecimento para desenvolver aplicações PHP usando as práticas mais modernas do mercado.
CONHEÇA A FORMAÇÃO

Timers

Timer são úteis para executar um determinado código em um momento futuro. Funcionam da mesma forma que setTimeout() and setInterval() do JavaScript.

Por exemplo, o Event Loop dispõe do método addPeriodicTimer() que faz com que o callback informado seja executado repetidamente a cada determinado intervalo de tempo.

Vamos criar o nosso primeiro exemplo? Tudo o que você precisa fazer é criar um diretório no local onde normalmente você escreve suas aplicações. Dentro dessa pasta, crie um arquivo composer.json com o seguinte conteúdo:

{
    "require": {
        "react/event-loop": "^1.1"
    }
}

Pelo terminal, acesse esse diretório e execute:

$ composer install

Por fim, no mesmo diretório, crie um arquivo index.php com o seguinte conteúdo:

<?php

require './vendor/autoload.php';

$loop = ReactEventLoopFactory::create();

$loop->addPeriodicTimer(1, static function () {
    static $count;

    if (null === $count) {
        $count = 0;
    }

    echo $count++ . PHP_EOL;
});

$loop->run();

// Output:

// 0
// 1
// 2
// 3
// 4
// 5
// ...

Para executar o exemplo:

$ php index.php

E você verá o resultado no seu terminal. A cada um segundo o callback (a função anônima que definimos no segundo argumento de addPeriodicTimer() é executada).

Outro método é o addTimer():

<?php

require './vendor/autoload.php';

$loop = ReactEventLoopFactory::create();

$loop->addTimer(2, static function () {
    echo 'World';
});

echo 'Hello ';
$loop->run();

// Output: Hello World

Nesse caso, o callback será executado uma única vez, num tempo futuro (em dois segundos).

Esse exemplo é, de certa forma, parecido com esse, escrito em JavasCript:

setTimeout(function () { 
  console.log('World');
}, 2);

console.log('Hello ');

Ambos os exemplos mostram que não estão seguindo o fluxo síncrono de execução, ademais, uma parte do código foi programada para ser executada em outro momento e isso não bloqueou a linha de execução.

Streams

A documentação do PHP define Streams como uma forma de generalizar arquivo, rede e outras operações que compartilham um conjunto comum de funções e usos. Em outras palavras, streams representam coleções de dados que podem não estar completamente disponíveis de imediato e também não possuem a limitação de ter que caber na memória, isso faz com que streams sejam uma ferramenta poderosa para lidar com grandes quantidades de dados que podem ser obtidas por partes (chunks). Grande parte do que é feito no PHP é sobre streams. Ao ler um arquivo, lidamos com streams. Ao retornar um output para o cliente, lidamos com streams. Ao obter dados de uma conexão TCP/IP, também estamos lidando com streams.

Temos três tipos de Streams:

  • Readable – Esse tipo permite ler (apenas leitura) os dados de uma fonte;
  • Writable – Esse tipo permite escrever (apenas escrita) dados em uma fonte;
  • Duplex (Readable e Writable ao mesmo tempo) – Esse tipo permite ler e/ou escrever (ambos) dados, como é o caso do protocolo TCP/IP (full-duplex).

Por exemplo, vamos supor que você precise avaliar linha a linha um arquivo de log que possui 1GB, se fizer assim:

$log = file_get_content("error.log")

O PHP tentará carregar o arquivo inteiro na memória (e enquanto não for carregado, nada mais pode ser executado, bloqueante por natureza), o que fatalmente acarretará em um erro e a execução do script será interrompida.

Usando a interface Readable Resource Stream do ReactPHP atingimos esse objetivo de forma não bloqueante, performática e com o mínimo uso de memória.

Vamos testar isso na prática? Criaremos um arquivo (na raiz do projeto) com os 10 milhões de números, um por linha, se você usa um sistema baseado em Unix, consegue atingir esse objetivo executando:

$ awk 'BEGIN { n = 1; while (n < 10000000) print (n++) }' > numeros.txt

No arquivo index.php, execute:

$content = file_get_contents('numeros.txt');

echo 'Memória utilizada: ' . (memory_get_peak_usage(true)/1024/1024);

// Memória utilizada: 77.24

O PHP tentará alocar cerca de ~78MB na memória. Se você tentar limitar o consumo de memória pelo script, terá um estouro:

<?php

ini_set('memory_limit', '12M');

$content = file_get_contents('numeros.txt');

// PHP Fatal error: Allowed memory size of 12582912 bytes exhausted (tried to allocate 78897112 bytes)

Agora vamos usar a classe ReadableResourceStrea do ReactPHP:

<?php

require './vendor/autoload.php';

use ReactStreamReadableResourceStream;

$loop = ReactEventLoopFactory::create();

$stream = new ReadableResourceStream(
    fopen('numeros.txt', 'rb'), $loop
);

$stream->on('data', function ($chunk) {
    // echo "$chunk
";
});

$stream->on('end', function () {
    echo 'Memória utilizada: ' . (memory_get_peak_usage(true)/1024/1024);
});

$loop->run();

// Memória utilizada: 2

O pico de consumo de memória ficou em 2MB (assim que a informação fica disponível no buffer, já a utilizamos, liberando-o). Poderíamos processar aí um arquivo bem maior, de dezenas ou centenas de gigabytes.

Veja que nesse exemplo implementamos dois eventos: data e end. No data recebemos os chunks (partes) do arquivo que está sendo lido. Em end executamos um callback quando o processo é finalizado.

Nesse exemplo usamos fopen(), função nativa do PHP (que trabalha com streams), mas em uma aplicação verdadeiramente assíncrona, ao invés disso, devemos usar o componente Filesystem do ReacPHP pois, se tiver uma disputa na leitura do arquivo, a aplicação pode ficar congelada (ler qualquer coisa do sistema de arquivos é bloqueante por natureza). Com esse componente, teríamos algo como:

<?php

require './vendor/autoload.php';

use ReactFilesystemFilesystem;

$loop = ReactEventLoopFactory::create();
$filesystem = Filesystem::create($loop);

$filesystem->file('numeros.txt')->open('rb')->then(function($stream) {
    $stream->on('data', function ($chunk) {
        // echo "$chunk
";
    });

    $stream->on('end', function () {
        //
    });
});

$loop->run();

Ah, para rodar esse exemplo é necessário que você instale o componente no seu projeto:

$ composer require react/filesystem

Se você já usou promises no JavaScript deve ter notado o método then() ali em cima. O conceito é o mesmo. O método open() retorna uma promise e no método then() executamos um callback quando ela (a “promessa”) é cumprida.

Lembra do exemplo que citamos lá no começo do artigo sobre uma tarefa que verifica se os links de um site estão online? Pois bem, ela poderia ser implementada usando a library reactphp-buzz, pois ela abstrai todo o essencial para se fazer requisições HTTP assíncronas.

Um protótipo de como isso poderia ser implementado de forma síncrona:

<?php

$time_start = microtime(true);

function urlsFromHtml(string $html) : array
{
    $dom = new DOMDocument();

    libxml_use_internal_errors(true);
    $dom->loadHTML($html);
    libxml_use_internal_errors(false);

    $urls = [];

    foreach ($dom->getElementsByTagName('a') as $node) {
        $urls[] = $node->getAttribute('href');
    }

    return $urls;
}

function getUrlStatusCode(string $url) : int
{
    $curl = curl_init();

    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD');
    curl_setopt($curl, CURLOPT_HEADER, 1);
    curl_setopt($curl, CURLOPT_NOBODY, true);
    curl_setopt($curl, CURLOPT_URL, $url);

    curl_exec($curl);
    $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    curl_close($curl);

    return $code;
}

function getUrlContent(string $url)
{
    $curl = curl_init($url);

    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    $html = curl_exec($curl);
    curl_close($curl);

    return $html;
}

$urls = urlsFromHtml(
    getUrlContent('https://www.globo.com')
);

foreach ($urls as $url) {
    $status = getUrlStatusCode($url) === 200 ? ' [online]' : ' [offline]';

    echo "{$url} -> {$status} 
";
}

echo 'Tempo total de execução: ' . round(microtime(true) - $time_start);

Dessa forma demora cerca de 150 segundos para “pingar” todas as URLS extraídas. Agora, a mesma implementação usando ReactPHP e e a library reactphp-buzz:

Primeiro instale a dependência dela no projeto:

$ composer require clue/buzz-react:^2.6
<?php

require './vendor/autoload.php';

use PsrHttpMessageResponseInterface;

$loop = ReactEventLoopFactory::create();
$browser = new ClueReactBuzzBrowser($loop);

function urlsFromHtml(string $html) : array
{
    $dom = new DOMDocument();

    libxml_use_internal_errors(true);
    $dom->loadHTML($html);
    libxml_use_internal_errors(false);

    $urls = [];

    foreach ($dom->getElementsByTagName('a') as $node) {
        $urls[] = $node->getAttribute('href');
    }

    return $urls;
}

$browser->get('https://www.globo.com')->then(function (ResponseInterface $response) use ($loop, $browser) {
    $urls = urlsFromHtml($response->getBody());
    foreach ($urls as $url) {
        $browser->head($url)->then(function (ResponseInterface $response) use ($url) {
            $status = $response->getStatusCode() === 200 ? ' [online]' : ' [offline]';

            echo "{$url} -> {$status} 
";
        });
    }
});

$time_start = microtime(true);

$loop->run();

echo 'Tempo total de execução: ' . round(microtime(true) - $time_start);

Já de forma assíncrona custou apenas 18 segundos. Lembrando que esse é apenas um exemplo para comparar a diferença entre os dois modelos.

Recomendação de leitura: Promises no ReactPHP

O outro artigo da série foi publicado, ele trata o uso de Promises com o ReactPHP. Você pode acessá-lo clicando aqui.

Concluindo

Esse foi um artigo introdutório sobre programação assíncrona e ReactPHP. Deu pra notar como é poderoso manipular streams, muitas possibilidades são abertas. Tem muito mais o que podemos explorar como Ticks, Promises, trabalhar com sistema de arquivos, trabalhar com websockets, usar funcionalidades do sistema operacional através de processos filhos, entre muitas outras coisas. Existem diversos projetos opensource desenvolvidos em cima do ReactPHP para atingir objetivos diversos, conforme você pode ver no site oficial.

Desenvolvedor PHP
Formação: Desenvolvedor PHP
Nesta formação você aprenderá todos os conceitos da linguagem PHP, uma das mais utilizadas no mercado. Desde de conceitos de base, até características mais avançadas, como orientação a objetos, práticas de mercado, integração com banco de dados. Ao final, você terá conhecimento para desenvolver aplicações PHP usando as práticas mais modernas do mercado.
CONHEÇA A FORMAÇÃO

Node

10 Truques do NPM – Você conhece todos?

Olá Web Developers!
Se você trabalha com JavaScript, provavelmente já teve que usar o NPM. Mas normalmente as pessoas usam essa ferramenta apenas para baixar bibliotecas ou frameworks.

Vamos ver alguns truques que podemos fazer com o NPM.

Npm - Gerenciador de pacotes para JavaScript
Curso de Npm - Gerenciador de pacotes para JavaScript
CONHEÇA O CURSO

1 – Crie ferramentas executadas diretamente da linha de comando

Com o NPM nós podemos criar ferramentas executadas diretamente do terminal inteiramente com JavaScript.
Já tivemos um post aqui ensinando como fazer isso, que você pode conferir neste link.

2 – Atalhos para os comandos mais comuns

É sempre bom saber o nome curto de um comando, permitindo escrever menos:

Comando Atalho
install i
list ls
test t
--global -g
--save -S
--save-dev -D

3 – Abrir o site do projeto

O comando home permite abrir o site de alguma biblioteca ou framework em seu navegador.

O código abaixo irá abrir o site do jQuery em seu navegador:
$ npm home jquery.

4 – Abrir o repositório do projeto

O comando repo permite abrir o site do repositório de alguma biblioteca ou framework em seu navegador.

O código abaixo irá abrir o repositório do lodash em seu navegador:
$ npm repo lodash.

5 – Procurar por pacotes não declarados no package.json

É um grande problema quando alguém esquece de indicar uma dependência no package.json. Mas não tem problema, pois com o seguinte comando você poderá ver quais são os pacotes utilizados no seu projeto que não estão listados:
$ npm prune

6 – Inicie um pacote rapidamente

O comando $ npm init nos permite iniciar um pacote, criando o arquivo package.json de acordo com certas respostas que damos às perguntas feitas.

Mas você pode pular as perguntas, fazendo com que o arquivo package.json seja criado imediatamente. Basta adicionar -y ao comando:
$ npm init -y

7 – Analisar dependências desatualizadas

Também podemos verificar se há dependências desatualizadas em nosso projeto. Basta executar o comando
$ npm outdated

8 – Trave a versão de suas dependências

Se quiser travar a versão das dependências do seu projeto, simplemente execute o comando
$ npm shrinkwrap

Isso irá criar um arquivo npm-shrinkwrap.json. Ao instalar as dependências do projeto, as versões definidas neste arquivo que serão levadas em consideração.

9 – Instalação de produção

No package.json nós podemos salvar as dependências em dependencies e devDependencies. Esta última é a lista de dependências usadas apenas durante o desenvolvimento.

Caso você não vá desenvolver nada onde está instalando seu projeto, com o seguinte comando é possível instalar as dependências do projeto, ignorando as dependências de desenvolvimento:
$ npm install --production

10 – Lista de pacotes instalados

Podemos ver uma lista de pacotes instalados com o comando:
$ npm ls --depth 0

Esse comando é ainda mais útil quando queremos saber quais módulos globais possuímos instalado em nossa máquina:
$ npm ls -g --depth 0

Conclusão

O NPM é uma ferramenta que muitos usam mas poucos sabem usar totalmente o seu poder. Para mais informações, confira nosso Curso de NPM.

Npm - Gerenciador de pacotes para JavaScript
Curso de Npm - Gerenciador de pacotes para JavaScript
CONHEÇA O CURSO

Node

Criando módulos globais do NPM

Olá Web Developers!

Que tal criar seus próprios programas que rodem diretamente do terminal usando JavaScript?
Com o NPM é muito simples de se fazer isso!

O que é NPM?

Se você trabalha com JavaScript, provavelmente já teve (ou terá em algum momento) que usar o Node.js. O gerenciador de bibliotecas mais comum é o NPM, que também pode servir para outras coisas, como mostramos em nosso curso de NPM.

Npm - Gerenciador de pacotes para JavaScript
Curso de Npm - Gerenciador de pacotes para JavaScript
CONHEÇA O CURSO

O que são módulos globais?

Além de códigos de terceiros, o NPM também nos permite instalar em nossa máquina programas que são executados a partir do terminal feitos inteiramente com JavaScript.

Esses programas, que podem ser executados pelo terminal de qualquer parte de seu computador, são chamados de módulos globais.
Os mais comuns são ferramentas como o Angular CLI, que nos ajudam a criar e gerenciar projetos Angular.

Imagine então poder criar um programa feito com JavaScript que pode ser facilmente instalado com o NPM e executado pelo terminal?
Você pode criar coisas como um servidor, um conversor de arquivos, um automatizador de tarefas, etc.

São várias possibilidades que vão depender da sua imaginação. Vamos criar um exemplo bem simples em 5 passos!

1 – Crie seu código principal

Vamos começar pelo código que será executado. Será bem simples: ele irá imprimir o texto “Olá Web Developers!”.

Para já deixarmos tudo organizado, vamos criar um diretório para nosso módulo. Eu vou chamar de meu-modulo.

Dentro deste diretório crie um outro diretório para guardar seus códigos. Isso não é obrigatório, mas nos ajuda a deixar tudo mais organizado.
Vamos criar um diretório chamado lib.

Dentro de lib vamos finalmente criar o arquivo que terá nosso código. Você pode dar qualquer nome. Vamos chamá-lo de index.js.
Terá o seguinte código:

function ola(nome){
    const texto = `Olá ${nome || 'web developer'}!`;
    console.log(texto);
}

exports.ola = ola;

Criamos um arquivo comum para Node.js. Temos uma função ola() que ao ser executada imprime “Olá web developer!” caso nenhum nome seja passado. Se tivermos algum nome, ele substituirá “web developer”.

No final exportamos esta função para poder utilizá-la em outro arquivo.

2 – Prepare o seu módulo

Vamos preparar nosso módulo agora.
No diretório raiz (meu-modulo), execute o comando $ npm init -y. Isso irá criar automaticamente o arquivo package.json para nós. Ele é muito importante para a sua biblioteca saber o que ela precisa instalar e os comandos disponíveis para serem executados.

Você pode editar o package.json para arrumar algumas coisas e adicionar mais informações. Ele ficará com o conteúdo parecido com o mostrado a seguir:

{
  "name": "meu-modulo",
  "version": "1.0.0",
  "description": "meu modulo global",
  "main": "./lib/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "hanashiro",
  "license": "MIT"
}
Npm - Gerenciador de pacotes para JavaScript
Curso de Npm - Gerenciador de pacotes para JavaScript
CONHEÇA O CURSO

3 – Crie os arquivos do módulo global

Agora chegou o momento de criarmos o arquivo que o terminal irá executar quando chamarmos nosso programa.

Primeiro crie um novo diretório chamado bin dentro de meu-modulo. Lembrando que isso não é obrigatório, estamos seguindo isso para tudo ficar mais organizado.

Dentro de bin crie o arquivo meu-modulo-legal.js com o seguinte conteúdo:

#!/usr/bin/env node

const ola = require('../lib/index.js').ola;
ola();

Nós simplesmente importamos a nossa função e a executamos. O que temos de diferente aqui é a primeira linha com o #!/usr/bin/env node.
Esta linha é fundamental para que o NPM gere um executável corretamente.

Lembre-se que este arquivo meu-modulo-legal.js que será executado.

4 – Configure a instalação global

Precisamos indicar qual o nome do comando ficará exposto para ser chamado no terminal e qual arquivo este comando deverá executar.

Para isso, adicione o seguinte código ao package.json:

{
    ...
    "bin": {
      "diga-ola": "./bin/meu-modulo-legal.js"
    },
    ..-
}

Isso permitirá que quando a gente abrir o terminal, possamos executar o comando diga-ola, e ele irá executar o arquivo meu-modulo-legal.js.

Lembre-se que as pessoas podem acabar instalando o seu módulo localmente. Se a sua ferramenta for algo que é melhor utilizada como um módulo global, você pode adicionar a seguinte linha ao package.json:

{
    ...
    "preferGlobal": true,
    ..-
}

Com esta linha o usuário ainda poderá instalar o seu módulo localmente, mas ele receberá um aviso indicando que é melhor instalar globalmente.

5 – Instale seu módulo globalmente para testá-lo

Agora só falta testarmos. Acalme-se, pois você não precisa publicar o seu módulo em algum repositório como o NPM ou Github para poder fazer o teste.

Para instalar globalmente o seu módulo, basta executar o comando npm install -g junto com o caminho do diretório que criamos (meu-modulo).

Ex: $ npm install -g C:\Users\seu-usuario\Desktop\meu-modulo

Ao instalar você já estará pronto para executar diretamente de seu terminal (de qualquer parte de seu computador) o comando $ diga-ola.

Extra: Parâmetros

Algo muito comum em programas executados pela linha de comando é permitir que a gente passe alguns parâmetros para configurar a função que será executada.

Lembra que nossa função permitia receber um nome? Pois vamos permitir que o usuário escreva o nome que ele quiser pela linha de comando.

Mude o arquivo meu-modulo-legal.js para ficar da seguinte maneira:

#!/usr/bin/env node

const args = process.argv.splice(process.execArgv.length + 2),
    nome = args[0];

const ola = require('../lib/index.js').ola;
ola(nome);

Primeiro pegamos os parâmetros passados no comando, e então pegamos o primeiro parâmetro e jogamos em uma constante chamada nome, passando-a para a nossa função ola().

Agora instale o seu módulo novamente para que o módulo instalado globalmente seja atualizado.

Se executar $ diga-ola você terá a resposta “Olá web developer!”, mas se executar $ diga-ola TreinaWeb terá “Olá TreinaWeb!”.

Conclusão

Agora você já sabe como criar seus próprios módulos globais, permitindo criar ferramentas com JavaScript e executá-las diretamente do terminal.

Agora basta você publicar no repositório do NPM para que outras pessoas possam usar.

Criou alguma ferramenta? Nos conte aí nos comentários!