NodeJS

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.

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.

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!

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.

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.

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.

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:

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.

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):

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!

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:

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).

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).

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:

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 = React\EventLoop\Factory::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.

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 = React\EventLoop\Factory::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 = React\EventLoop\Factory::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 React\Stream\ReadableResourceStream;

$loop = React\EventLoop\Factory::create();

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

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

$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 React\Filesystem\Filesystem;

$loop = React\EventLoop\Factory::create();
$filesystem = Filesystem::create($loop);

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

    $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} \n";
}

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 Psr\Http\Message\ResponseInterface;

$loop = React\EventLoop\Factory::create();
$browser = new Clue\React\Buzz\Browser($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} \n";
        });
    }
});

$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.

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.

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.

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.

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"
}

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!

Criando APIs com o Strapi.io

O uso de APIs vem em constante crescimento nos últimos anos, e isso se deve a grande quantidade de aplicações clientes que precisam consumir dados de um mesmo banco de dados. Essas aplicações clientes vão desde aplicativos desktop até aplicações mobile, fazendo com que mais e mais serviços sejam criados diariamente.

Devido a esse cresimento, cada dia mais tecnologias surgem para auxiliar os desenvolvedores a criar estas APIs. Uma destas tecnologias é o Strapi.io e é sobre ela que vamos falar neste artigo.

O que é o Strapi.io?

O Strap.io é uma ferramenta que visa facilitar a criação de serviços REST através de um painel administrativo totalmente funcional. Desenvolvido em node.js, o Strap.io é open-source e bem simples de se utilizar. Toda a sua documentação pode ser encontrada em sua página principal.

Instalando o Strapi.io

Para instalar o Strapi.io, precisamos apenas do node em nosso computador. Para isso, podemos baixar seu instalador na página oficial da ferramenta. O processo de instalação é bem simples e intuitivo, o famoso “next, next, finish”.

Após instalar o node, estamos aptos a instalar o strapi.io. Para isso, no Terminal (ou CMD, se você usa o Windows), digitamos o seguinte comando: npm install strapi@alpha -g. O comando vai baixar e instalar o strapi em seu SO e o retorno deve ser o seguinte:

Criando o projeto

Após instalar o Strapi.io, chegou a hora de criar nosso projeto. Para isso, abrimos o terminal e digitamos o seguinte comando: strapi new my-project. Este comando irá criar um projeto novo do strapi com o nome “my-project”.

Durante o processo de criação do projeto, precisamos selecionar qual banco de dados vamos utilizar (MySQL, Postgree ou MongoDB), o nome do BD, o Host, Porta de acesso para o BD, nome de usuário e senha do banco de dados selecionado, como mostra a imagem abaixo:

Feito isso, navegamos até seu diretório e executamos seu servidor com os comandos abaixo:

cd my-project
strapi start

Feito isso, seremos redirecionados para a página de registro de usuários do strapi, uma área administrativa com a seguinte URL: http://localhost:1337/admin/plugins/users-permissions/auth/register. Nela inserimos os dados de cadastro e clicamos no botão “Pronto para começar”, conforme visto na imagem abaixo:

Criando nossa API

Após configurar o usuário de acesso à área Administrativa, somos redirecionados para o dashboard do Strapi, onde temos acesso a todos os seus recursos. Agora, o primeiro passo é criar as tabelas do banco de dados que irão armazenar os dados da nossa aplicação. Para isso, clicamos no link “Construtor de conteúdo”. É lá que criamos as tabelas que vamos utilizar em nosso serviço REST:

Na próxima tela, selecionamos a opção “Adicionar Tipo de Conteúdo” e determinamos as configurações da tabela a ser criada:

Feito isso, clicamos no botão “Adicionar Novo Campo” e determinamos os campos que nossa tabela irá possuir, assim como seu tipo. O Strapi provê diversos tipos de campos, conforme visto abaixo:

Ao selecionar o tipo do campo, determinamos o nome do campo e clicamos em “continuar”. Vale lembrar que cada campo tem suas configurações particulares, então a tela abaixo pode variar conforme o tipo de dados que você está criando:

Após determinar todos os campos da tabela, clicamos no botão “Salvar” e a tabela será criada no banco de dados que configuramos anteriormente.

Com isso, estamos quase prontos para consumir nossa API. O último passo é configurar o nível de acesso e quem é permitido a consumir os recursos da API. Para isso, vamos até o menu “Papeis e Permissões” e selecionamos a permissão “Public”.

Observação: Esta permissão é responsável por configurar o acesso público da nossa API, ou seja, qualquer um que possuir a rota da nossa API, poderá consumir nosso serviço REST, então é muito importante que você reveja a necessidade do seu projeto antes de liberar a sua API para o público. Como estamos em um ambiente de desenvolvimento, não vamos nos atentar a isso.

Agora, na sessão “Permissões”, determinamos os métodos que os usuários que possuírem a permissão pública poderão efetuar em nossa tabela de clientes:

Vamos deixar todas as opções como ativas. E é isso, finalmente temos nosso serviço de manipulação de clientes ativo e funcionando, chegou a hora de testar!

Testando nosso serviço

Agora podemos testar nossa API de clientes. Para isso, é só fazer uma requisição para a rota http://localhost:1337/clientes. Neste teste, vou utilizar o Postman, uma ferramenta para envio de requisições HTTP.

Sendo assim, enviando uma requisição POST para a rota mencionada acima e passando a idade e o nome no corpo da requisição, temos um novo cliente cadastrado, como podemos ver nas imagens abaixo:

Podemos também fazer todos os outros métodos básicos de um CRUD, como editar e remover um cliente do nosso banco de dados utilizando, respectivamente, os métodos PUT e DELETE:

Concluindo

Então é isso. Neste artigo vimos uma ótima ferramenta para criação de APIs de forma fácil, rápida e intuitiva. Vale ressaltar que esta ferramenta possui todos os métodos para controle de acesso e permissão e que tudo isso pode ser visto na sua documentação oficial

O que se pode fazer com JavaScript hoje em dia?

Olá, Web Developers!

O JavaScript inicialmente foi criado como um complemento para o navegador da Netscape. Por muito tempo foi visto como uma linguagem ruim, bagunçada e lenta (e estavam certos).

Mas o JavaScript evoluiu muito. Hoje em dia é bem mais organizado, rápido e possui várias funcionalidades que nos facilitam criar várias coisas. Além disso, a linguagem não se limita mais apenas aos navegadores.

Vamos conhecer algumas áreas onde podemos atuar com JavaScript além do seu uso comum e até além dos navegadores. Dessa maneira você poderá usar seus conhecimentos em JavaScript e escolher entrar em alguma área além da web, como desenvolvimento de jogos, robótica, automação, etc.

Observação: esse post não incentiva o uso de JavaScript para tudo. Há de se ponderar a melhor ferramenta/tecnologia para o problema que se precisa resolver.

Node.js

Node.js

Não tem como começar essa lista sem falar do Node. Ele é basicamente uma ferramenta que executa o JavaScript fora do navegador. Quando instalado, chamamos o Node.js pelo terminal. Como não estamos executando o JavaScript em um navegador, os cuidados com segurança são diferentes.

Há várias funcionalidades disponibilizadas para o Node.js, fazendo com que possamos fazer coisas com JavaScript que não conseguimos fazer quando o nosso ambiente é o navegador.

Um exemplo é o acesso a arquivos. Um código JavaScript no navegador não consegue acessar os arquivos do usuário, pois seria uma falta de segurança você entrar em um site e ele vasculhar seu computador.Como o Node.js está instalado em sua máquina, o JavaScript tem liberdade de acessar os arquivos contidos no HD.

O Node.js possibilitou a criação de várias outras ferramentas para podermos usar JavaScript fora do navegador, e hoje em dia é um conhecimento obrigatório para quem quer trabalhar com JavaScript.

https://nodejs.org/

npm

O Node.js vem com o “npm” (node package manager). Com ele podemos gerenciar as dependências de nossos projetos.

Um exemplo: caso queira usar jQuery em seu projeto, ao invés de ter o trabalho de fazer download do jQuery, basta executar o comando:

$ npm install jquery

Caso queira atualizar a versão do jQuery em seu projeto, basta executar o comando:

$ npm update jquery

O npm também pode ser usado para instalar outros programas que podem nos auxiliar no desenvolvimento de nossas aplicações e executar comandos.

https://www.npmjs.com/

Testes

Quando estamos escrevendo código é preciso fazer muitos testes para assegurar que nossas funções estão retornando o valor esperado. É bom sempre testar a mesma função passando vários valores diferentes, inclusive valores que a função não aceita para ver se há necessidade de criar um tratamento para a aplicação não quebrar.

Acontece que qualquer alteração em uma função pode afetar outras funções. Então há ferramentas que facilitam a criação de testes.

Os mais famosos são: QUnit, Mocha e Jasmine.

Qualidade de Código

Quando estamos escrevendo nosso código, é muito importante mantê-lo com qualidade. Uma característica de um código de qualidade é mantê-lo uniforme.

Isso inclui sempre identar o código, padronizando a quantidade de espaços ou se será usado “tab”, se ao criar uma função você irá abrir chaves “{“ na mesma linha ou em uma linha nova, etc.

Há ferramentas como o JSLint e JSHint que analisam o nosso código e indicam se estamos mantendo as regras que foram definidas.

Automatização de Tarefas

Quando estamos desenvolvendo é comum precisarmos realizar certas operações para melhorar o nosso código. Um exemplo é minificar o nosso código, fazendo com que os arquivos fiquem menores, o que faz a aplicação ser carregada mais rapidamente pelo navegador.

Outras tarefas podem ser a execução de testes de qualidade, como o JSLint, ou testes de software, como o Jasmine.

Para nos auxiliar temos os automatizadores de tarefas. Os mais famosos são o Grunt e o Gulp. Podemos definir qualquer tarefa e pedir para que estas ferramentas as executem para nós.

Imagine que quando salvamos um arquivo, ele deve ser testado pelo Jasmine, analisado pelo JSLint e, se todos os testes passarem, iremos minificar o arquivo.

E mais! Podemos querer também que se tudo der certo, ele envie um E-mail para o nosso cliente dizendo que em breve iremos lançar uma nova versão da nossa aplicação!

Eles nos ajudam a automatizar o que quisermos, o limite é sua imaginação!

Servidores

Normalmente os códigos escritos no lado do servidor são feitos com linguagens como Java, PHP, Ruby, etc.

Com o Node.js foi possível começar a escrever código para o servidor com JavaScript. Já houve outras tentativas, mas o Node.js teve mais sucesso.

Os frameworks mais conhecidos para Node.js são: Express, Hapi e Koa.

Também podemos criar código Back End com o Meteor. O Meteor é uma plataforma de desenvolvimento fullstack (front e back end).

Bancos de Dados

O MongoDB é um banco de dados orientado a documentos. O console que usamos para acessar os dados executa JavaScript. Então se você sabe JavaScript, pode ter facilidade em aprender a gerenciar o MongoDB.

https://www.mongodb.com/

Aplicativos Mobile

Com JavaScript também podemos criar aplicativos mobile. A vantagem é usar uma única linguagem de programação para as diferentes plataformas. O modo mais conhecido é usando o Cordova/PhoneGap. Com eles nós criamos aplicações híbridas.

São chamadas de “híbridas” porque unem duas tecnologias diferentes. Por exemplo, o código nativo do Android é escrito em Java. Com o Cordova nós criamos aplicações web comuns, com HTML+CSS+JavaScript. Para acessar funcionalidades do dispositivo, há uma integração do código nativo em Java e nós acessamos essas funcionalidades pelo JavaScript.

Quando abrimos o aplicativo, estamos na verdade abrindo um navegador interno que irá apresentar a nossa aplicação e que pode acessar funcionalidades do dispositivo que não poderíamos acessar a partir de uma aplicação web comum.

Então a ideia de “híbrido” é porque estamos juntando tecnologia web com a tecnologia nativa.

http://facebook.github.io/react-native/

Também podemos criar aplicações nativas. As ferramentas mais conhecidas são o React Native e o NativeScript. Com eles nós criamos telas com XML ao invés de HTML, e podemos estilizar com CSS. Isso será convertido para uma tela nativa de cada plataforma, como Android e iOS. Já as ações são escritas com JavaScript mesmo.

A vantagem disso é que, por ser uma aplicação nativa ao invés de uma aplicação com um navegador, teremos melhor performance do que uma aplicação híbrida.

Outra vantagem é que as aplicações híbridas dependem do navegador padrão do sistema, então corremos o risco de criar um código que o navegador daquele dispositivo não suporte. Em uma aplicação nativa, já que não dependemos de navegadores, não precisamos nos preocupar se haverá suporte para as funcionalidades ou não.

Softwares Desktop

Com o Node.JS também é possível criar aplicações Desktop. Estas aplicações normalmente utilizam o Chromium, navegador de código aberto que está por trás do Google Chrome, e o Node.js.

As telas são feitas com HTML5 e CSS3, e o JavaScript pode se comunicar diretamente com o Node.js, que fica embutido na aplicação.

Atualmente o framework mais utilizado é o Electron, criado pela equipe do GitHub. Com ele já foram desenvolvidos famosos softwares como o Atom, Slack e Visual Studio Code.

https://electronjs.org/

SmartTVs

Também é possível criar aplicativos para SmartTVs. Você irá também usar HTML/CSS/JavaScript. As que dão maior suporte para isso são as TVs da Samsung.

3D

A partir do elemento canvas do HTML5 também temos uma API chamada WebGL. Ela nos ajuda a trabalhar com renderização de gráficos 2D e 3D.

Uma biblioteca que nos ajuda a trabalhar com 3D com JavaScript é o three.js.

https://threejs.org/

Jogos

Também é possível criar jogos com JavaScript. Isso graças ao elemento canvas do HTML5, que nos permite desenhar na tela com JavaScript.

Há várias bibliotecas que nos ajudam a criar jogos com JavaScript.
Uma das mais famosas é o Impact, que é paga. Uma outra famosa, que é gratuita, é o Phaser.

Como os jogos apenas usarão o elemento canvas do HTML5 e JavaScript, você poderá criar jogos para qualquer lugar que os suporte ou possua um navegador.

Pode-se usar ferramentas como o Cordova/Phonegap, mas também há outras ferramentas que ajudam a otimizar jogos feitos com JavaScript para dispositivos móveis.

Plugins

Há softwares que possibilitam a criação de plugins. Isso permite que as pessoas criem novas funcionalidades para eles. Normalmente esses plugins são escritos com linguagens de scripts.

Um exemplo é o PhotoShop. Ele aceita a criação de plugins escritos em JavaScript.

Sistemas Operacionais

Um sistema baseado no Node.js, escrito apenas com JavaScript, foi desenvolvido. É o NodeOS.

Qualquer pacote do npm é um pacote do NodeOS, o qual conta com mais de 475.000 pacotes. O objetivo do NodeOS é fornecer apenas o necessário e o npm cuida do resto. Já que qualquer um pode contribuir com o npm, qualquer um pode criar pacotes para o NodeOS.

Você pode ver mais em:

http://node-os.com/

Programação de Hardwares e Internet das Coisas

Com o Node.JS também é possível controlar hardwares. Podemos então usar o JavaScript para trabalhar além das telas dos computadores e celulares, como controlar drones.

Ao programar placas, podemos criar códigos para até mesmo integrar vários objetos, o famoso “Internet das Coisas” (IoT).
Imagine usar JavaScript para controlar as lâmpadas, portas e janelas de sua casa de acordo com a iluminação do ambiente. Ou que quando o GPS do seu smartphone perceber que você está chegando em casa ou no trabalho, ligue a cafeteira para que o café esteja pronto quando você chegar.

Um exemplo é o noduino, um framework para acessar os controles básicos do Arduino a partir de aplicações web usando HTML5, Socket.IO e Node.JS.

Também há o projeto Tessel. No próprio site você pode comprar as placas para montar o seu produto.

Outro famoso projeto é o Cylon.js. Ele é um framework voltado para robótica, computação física e Internet das Coisas.

Você pode ver mais em:

Hologramas

O JavaScript também é capaz de ser utilizado com hologramas.
Um exemplo é o HoloJS, da Microsoft, que é um framework para criar aplicações holográficas usando JavaScript e WebGL.

Realidade Virtual e Realidade Aumentada

Muito se fala sobre Realidade Virtual e Realidade Aumentada. Um exemplo para realidade aumentada é o JavaScript ARToolKit.

https://github.com/artoolkit/jsartoolkit5

Inteligência Artificial

Também podemos aproveitar o JavaScript na área da inteligência artificial. Há muitas bibliotecas e exemplos.

Uma biblioteca usada para isso é a “deeplearn.js”, que você pode conhecer melhor em:

https://deeplearnjs.org/ .

Concluindo

Hoje em dia várias linguagens de programação tentam estar onipresentes, como é o caso do JavaScript. Porém, nem sempre é algo bom. Não existe “bala de prata”. Há linguagens que estão mais evoluídas e adaptadas para certas áreas.

Mesmo que JavaScript seja minha linguagem principal, há momentos em que prefiro usar outras linguagens de programação, pois admito que possuem ferramentas melhores para se trabalhar e acabam entregando algo com mais rapidez e qualidade.

A vantagem que enxergo em utilizar uma única linguagem em várias áreas é possibilitar que uma pessoa com conhecimento em uma linguagem possa experimentar novas áreas, ou que uma pequena empresa possa reaproveitar os conhecimentos da equipe e códigos para trabalhar com outras tecnologias.

Node.js: por que você deve conhecer essa tecnologia?

Você provavelmente já ouviu falar de Node.js: a tecnologia figurou na Stack Overflow Developer Survey de 2017 entre as mais amadas entre desenvolvedores (em segundo lugar, com 62.1%) e a que os devs mais querem trabalhar (em primeiro lugar, 22.2%). A pesquisa do Stack Overflow também demonstrou que Node.js vem crescendo de forma impressionante: de 2013 a 2017, passou de 8% a 26% em popularidade.

Dentre os principais motivos desse crescimento está a comunidade dedicada ao Node.js. Por ser uma tecnologia open source, o Node.js mantém uma comunidade ativa e muito presente: é possível acompanhar (e contribuir) via GitHub, auxiliar com a documentação ou mesmo produzir tutoriais. No Brasil, já existem duas grandes comunidades dedicadas ao Node no Google+ e Facebook, além dos Meetups espalhados por diversas cidades, que são organizados pelos próprios desenvolvedores e interessados. Além disso, em 2016 também rolou a primeira Nodeconf BR, evento dedicado a discutir essa tecnologia.

Nesse post vamos falar um pouco sobre o que é Node.js, em quais casos é melhor utilizá-lo e que projetos já adotaram. Mas e a primeira pergunta, que não quer calar: o que é Node?

O que é o Node.js?

Nem linguagem, nem framework: Node.js é um runtime de JavaScript que leva a renderização e processamento do código JavaScript para o lado do servidor, desvinculando-o totalmente do browser, possibilitando que você desenvolva aplicações de rede rápidas e estáveis.

Desenvolvido sobre o motor JavaScript V8 – engine criada pelo Google e utilizada nos navegadores Chrome e Chromium (open source), Node.js é direcionado a eventos (event driven) e o processamento de requisições I/O (input e output) é não-bloqueante, garantindo estabilidade e pouco consumo de recursos. O desenvolvimento da tecnologia teve como objetivo “fornecer uma maneira fácil para construir aplicações escaláveis”, conforme o site oficial.

Criado em 2009 por Ryan Dahl, o Node.js, apesar de recente, já é utilizado por grandes empresas como LinkedIn, Groupon e PayPal (mais para frente você poderá ver mais sobre como essas empresas estão usando Node). Entre as principais vantagens está justamente o uso de JavaScript: uma linguagem versátil que possui uma curva de aprendizado relativamente baixa, além de ser uma das linguagens mais populares na atualidade.

Quando devemos utilizar o Node.js?

Levando em consideração esses pontos positivos, pode ser fácil se empolgar e já começar a querer mudar todos os seus projetos para Node. Apesar de estar fazendo a diferença para muita gente, Node.js não é a solução para todos os problemas. Existem casos em que optar por essa tecnologia é uma boa, mas outros em que ela talvez não te ajude tanto.

Por isso, Node é a pedida caso você necessite:

Criar aplicações de chat e mensagens instantâneas:

Apps como chats e ferramentas de colaboração multi-usuários tornam-se um dos melhores usos para Node: alto tráfego e uso intenso de dados em tempo real rodando em diferentes dispositivos. Como o Node.js consegue lidar muito bem com comunicação assíncrona e já consegue lidar com WebSockets (através de bibliotecas como Socket.IO), ele se mostra como uma das melhores opções neste tipo de cenário. Um exemplo de chat construído usando Node.js é este, que usa Socket.io e Express;

Criar APIs que sejam facilmente escaláveis:

Também por decorrência da abordagem não-bloqueante do Node.js, ele pode ser uma excelente opção quando for necessário criar APIs que sejam escaláveis de maneira rápida e simples. Em tempos onde a criação de aplicações SPA (Single Page Application) é muito comum, a questão da escalabilidade e do tempo de resposta de APIs (RESTful ou não) torna-se importantíssima; já que, nesse tipo de cenário, existe uma grande possibilidade de que o I/O de rede cresça consideravelmente e, portanto, o servidor tenha que lidar com uma quantidade de requisições maior ao mesmo tempo sem perder performance ou consumir grandes quantidades de recursos (como memória e processador) do servidor. O Node.js já foi criado exatamente para resolver esse tipo de situação;

Dados em streaming:

Outro bom uso do Node.js é para dados em streaming: novamente, por causa da sua abordagem de I/O não bloqueante. O Node.js é uma opção que economiza muito tempo e recursos para processar dados de fontes diversas. Na verdade, em quaisquer situações onde haja uma carga muito grande de leitura e escrita de dados (como acontece com streaming), o Node.js conseguirá entregar um resultado muito satisfatório com menos esforço, tanto no sentido de codificação, como no sentido de hardware necessário para suporte;

IoT:

Uma pesquisa realizada pela Node.js Foundation em 2016 mostrou que 96% dos envolvidos em projetos de IoT estão usando JavaScript/Node para o desenvolvimento. E os desenvolvedores de IoT também são os mais experientes, com 40% dos participantes tendo mais de 10 anos de experiência.

Outros casos de uso para o Node.js que podem ser interessantes:

Criação de dashboard para monitoramento de aplicações, criação de sistemas para corretores de ações e até mesmo backend para jogos!

Agora, é melhor não usar Node.js se…

Sem dúvida, a utilização de Node pode trazer muitos benefícios, especialmente em termos de performance, mas existem casos em que não vale a pena adotar essa tecnologia. Por exemplo, para projetos que precisam de muitos recursos de CPU, Node.js não é a melhor escolha, já que as vantagens do modelo de I/O não bloqueante e eventos lineares são perdidas nesse caso.

Se você, portanto, está criando algoritmos de machine learning, ou se a aplicação a ser construída demanda regras de negócio complexas que normalmente seriam resolvidas com orientação à objetos, Node também não é a melhor opção por causa de características do próprio JavaScript.

O Node.js também tem uma limitação importante: não é possível utilizar multi-threading com ele, pelo menos não da maneira tradicional – se você quiser trabalhar com multithreading “de verdade” no Node.js, precisará de bibliotecas adicionais. Apesar de o Node.js trabalhar sob o aspecto não-bloqueante, é importante ressaltar que todo o código JavaScript que é convertido para código de máquina é executado em uma única thread.

O Node.js até possui algumas bibliotecas que permitem a junção entre processos concorrentes em um único cluster, simulando o multithreading. Porém, mesmo com estas bibliotecas, o compartilhamento de informações entre os diferentes processos que estão sendo executados de maneira concorrente é algo muito complexo de ser realizado. No final, o Node.js é ótimo para multi-processos, mas não para multithreading. Sendo assim, se você precisa de paralelismo, onde cada uma das unidades de trabalho precisa compartilhar informações entre si, o Node.js pode não ser uma opção tão interessante.

Projetos e empresas que utilizam Node.js

No seu Guia para Convencer seu Chefe a adotar Node.js, Felix Geisendörfer fala que muitas vezes a barreira para adotar uma nova tecnologia open source é a dúvida sobre o comprometimento a longo prazo com a manutenção. No caso do Node, essa questão foi respondida com a Node Foundation e o apoio de grandes empresas, como Microsoft, Joyent e IBM, que garantem o desenvolvimento da tecnologia ao longo dos anos.

A maior prova de que Node.js está aqui para ficar é a quantidade de grandes projetos que já adotam a tecnologia:

LinkedIn: O LinkedIn foi uma das primeiras grandes empresas a adotar Node.js em 2011. O app mobile lançado pela rede social que usava Node conseguiu aumentos de performance com um uso muito menor de recursos. Além disso, de acordo com Kiran Prasad, líder de desenvolvimento mobile do LinkedIn na época, o projeto foi desenvolvido de forma muito rápida;

PayPal: O PayPal mudou parte de suas aplicações de Java para Node.js em 2013 ao perceber que, com Node e a possibilidade de escrever tudo em JavaScript, eles poderiam ganhar mais produtividade e performance. O PayPal inclusive criou o Kraken.js, hoje open source, uma layer de convenções e configurações criada para dar mais estrutura a projetos criados em Express;

Groupon: O Groupon mudou do Ruby on Rails para o Node.js ao perceber que a aplicação monolítica que tinham estava difícil de manter e escalar. Além disso, após realizarem aquisições na Europa e América do Sul, era importante para a empresa ter uma plataforma de desenvolvimento para toda as equipes de desenvolvimento, independente da localização. Além dos ganhos de performance nas páginas do site, o Groupon também percebeu um aumento na velocidade da entrega de novas features por parte dos times de desenvolvimento;

Ghost: A plataforma Ghost de blogging que hoje também é voltada ao jornalismo independente, roda sob o Node.JS. Desde à sua criação, já foram feitos quase dois milhões downloads. Ela é open source e mantida por uma organização sem fins lucrativos.

Tecnologias utilizadas em conjunto com Node.js

Em 2016, a *Node.js Foundation, *em parceria com a Linux Foundation, criou uma pesquisa sobre o uso de Node.js, que foi respondida por 1760 pessoas ao redor do mundo. A pesquisa demonstrou quais são as principais tecnologias utilizadas junto com o Node.js:

  • MongoDB é a primeira escolha para banco de dados. Dois terços dos participantes afirmaram que MongoDB é o banco utilizado com suas aplicações Node, pois ambas são tecnologias não-bloqueantes que aguentam um alto throughput de I/O.

  • Containers são a tecnologia perfeita para rodar aplicações em Node.js. Quase metade dos participantes está usando container. Dentre as opções, Docker é a principal escolha – 73% de quem usa alguma tecnologia baseada em containers, está usando Docker.

Na Umbler, você consegue hospedar uma aplicação Node.js usando tecnologia de Containers e tem MongoDB disponível para criar com as melhores ferramentas.

Você já utiliza Node.js? Tem interesse em usar? Conte para a gente nos comentários o que você acha dessa tecnologia.

Usando o Async / Await do JavaScript

Olá, Web Developers!

Vamos falar hoje sobre a possibilidade de criar funções assíncronas e como trabalhar com elas de forma bem simples com as novas instruções async e await do JavaScript.

Vamos imaginar a seguinte situação:

function func1(number){
    return new Promise(resolve =>{
        setTimeout(() => resolve(77 + number) , 1000)
    })
}

function func2(number){
    return new Promise(resolve =>{
        setTimeout(() => resolve(22 + number) , 1000)
    })
}

function func3(number){
    return new Promise(resolve =>{
        setTimeout(() => resolve(14 * number) , 1000)
    })
}

// Usando as funções
func1(4)
   .then(number => {
        console.log('Hello');
        return func2(number);
   })
   .then(number => {
        console.log('World');
        return func3(number);
   })
   .then(result => console.log(result))

Usar essas funções assíncronas com Promises nos poupam do “Callback Hell”, mas mesmo assim, precisamos passar uma função de Callback para cada then().

Com async e await podemos trabalhar com código assíncrono em um estilo mais parecido com o bom e velho código síncrono. Vamos reescrever o exemplo anterior, partindo da parte onde consumimos as nossas três funções assíncronas:

async function myAsyncFunction(){
    var number = await func1(4);

    console.log('Hello');
    number = await func2(number);

    console.log('World');
    var result = await func3(number);

    console.log(result);
}

Primeiro de tudo, temos o comando async. Ele faz com que uma função automaticamente retorne uma Promise. Então:

// retorna o número 5
function example1(){
    return 5;
}

// retorna uma Promise com o valor 5
function example2(){
    return Promise.resolve(5);
}

// faz exatamente o mesmo que a função "example2()"
async function example3(){
    return 5;
}

Depois, dentro de myAsyncFunction(), veja que usamos o comando await antes de chamar as funções que retornam Promises:

async function myAsyncFunction(){  
   var number = await func1(4);

Veja que, ao invés de usar o then() com uma função de callback, nós simplesmente estamos jogando o retorno da função em uma variável, como fazemos com códigos síncronos.

O comando await basicamente pausa a função myAsyncFunction() até que a Promise dentro da função func1() seja resolvida. Então o valor retornado é atribuído à variável e o código de myAsyncFunction() continua de onde parou.

Lembre-se: o comando await só pode ser executado dentro de uma função marcada como async. Por isso que para executar as funções eu criei a função myAsyncFunction().

Como estamos falando de funções assíncronas, não pense que essa “pausa” que o await causa irá travar o seu código todo. Lembre-se que essa é só uma nova sintaxe para se trabalhar com Promises. Então, quando o await pausar a sua função, outras funções poderão estar sendo executadas.

Devo usar Async / Await ao invés de then()?

Isso vai depender muito da situação. Muitos desenvolvedores vão pelo gosto, outros definem um padrão único a se seguir para o projeto e outros decidem o que é melhor para cada caso.

Em meus projetos eu prefiro usar o que irá permitir uma melhor leitura e entendimento do código. No exemplo lá em cima, vimos um caso em que usar as funções com await deixou o código mais legível do que ficar criando vários then() (apesar de sempre atribuir o valor na mesma variável).

Vamos a um exemplo em que o then() pode deixar nosso código mais legível:

function example1(){  
    return func1(4)
        .then(func2)
        .then(func3);
}

async function example2(){  
    var number = await func1(4);
    number = await func2(number);
    number = await func3(number);
    return number;
}

// ou
async function example3(){  
    return func3(await func2(await func1(4)))
}

O example2() e example3() usam o async/await. Veja que, nesse caso que só encadeamos funções, o uso do then() em example1() acaba sendo bem mais simples que example2() e mais legível que example3().

Sem contar que o comando await só funciona dentro de uma função async. Se estivermos dentro de uma função que não é async, teríamos que criar uma função async apenas para poder executar o comando await, enquanto as Promises podem ser usadas em qualquer lugar.

Concluindo

Async / Await aumentam as nossas possibilidades, mas também temos que analisar quando é o melhor momento para utilizá-los.

Os navegadores modernos e o Node.js já dão um bom suporte para eles, então, aproveite (com moderação)! 😉

JUNTE-SE A MAIS DE 150.000 PROGRAMADORES