ReactiveX é uma tecnologia relativamente nova que, gradativamente, tem moldado a forma de Aplicações Web modernas. Nesse artigo, vamos entender o que é fluxo de dados em tempo real e como são implementadas com ReactiveX.

Atualmente, vivemos a chamada Era da Informação, que tem como uma das principais características o ritmo rápido e acelerado dos acontecimentos. Todos nós queremos velocidade. Ou melhor, instantaneidade, para não dizer “em tempo real”. É nesse contexto, cercados de dispositivos baseados nas mais diferentes tecnologias, que surge o ReactiveX, projetado para programação reativa com obervables, com potencial para ser utilizado nas mais diversas plataformas.

Falar de realtime é, antes de tudo, falar de assincronismo. Por isso, antes de explicar o que é ReactiveX, é importante entender um pouco a evolução do assincronismo.

É importante observar que nesse artigo veremos alguns exemplos em Javascript, mas que podem ser reproduzidos na linguagem de sua preferência.

Assincronismo?

Basicamente, chamamos de assíncrono aquilo que não ocorre ao mesmo tempo. Por exemplo, quando um computador lê um código, ele inicia a execução na primeira linha do arquivo e vai executando uma de cada vez. Imagine o código a seguir:

funcaoAssincrona();
funcaoA();
funcaoB();

Cada linha faz uma chamada a uma função. Considere que somente a primeira função é assíncrona e as outras duas, não. A funcaoAssincrona será executada, assim como a funcaoA e a funcaoB. Porém, o computador não se certificará que a função assíncrona foi finalizada antes de seguir em diante e executar as outras funções. Ela poderá continuar sendo executada enquanto as outras também serão, paralelamente.

Para funções que não são assíncronas, porém, o computador só irá para a próxima linha após a sua execução ter finalizado.

O lado bom dessa abordagem é o ganho de velocidade, pois não é preciso que o computador espere uma tarefa que pode demorar algum tempo para ser executada para, então, seguir o fluxo do código. Como, por exemplo, uma requisição a um servidor para receber dados.

Contudo, o problema dessa abordagem é quando o código apresenta alguma dependência com uma tarefa que já precisava ter sido realizada por uma função assíncrona anterior. Como saber se a execução da função assíncrona foi finalizada? Nesse contexto, surgem as funções callbacks.

Função callback

Uma callback é uma função que será invocada em resposta a uma chamada assíncrona que foi finalizada. Observe o exemplo abaixo:

function callback(dados){
    // faz alguma manipulação nos dados
}

receberDados(urlDoServidor, callback);

Estamos invocando uma função assíncrona chamada receberDados, que irá realizar uma requisição para o servidor indicado pelo endereço no parâmetro urlDoServidor. O parâmetro callback é uma função que irá ser invocada logo após essa tarefa assíncrona ser finalizada, isto é, quando a resposta do servidor finalmente chegar com os dados. Então, basicamente, haverá uma espera para os dados chegarem, para, depois, a callback ser invocada tendo os dados recebidos como parâmetro. Enquanto isso, o computador segue executando as demais linhas do código.

A vantagem disso é que o código que tiver dependência com a tarefa que é executada de forma assíncrona pode ser posto dentro da callback.

Essa abordagem pode parecer perfeita, mas não é… O problema surge quando a aplicação se torna complexa, a ponto de termos que colocar callbacks dentro de callbacks, que, por sua vez, estão dentro de outras callbacks e assim por diante. Surge o chamado Callback Hell. Fica muito difícil realizar a leitura do código, a manutenção e a manipulação de erros.

Logo, uma nova abordagem surge para lidar com assincronismo: as Promises.

Promises

O bacana das Promises é que com elas podemos lidar com assincronismo de forma que facilite a leitura do código e manipulação de erros. Elas também retornam valores, permitindo que várias Promises possam ser encadeadas umas com as outras, evitando algo semelhante ao Callback Hell. Observe este exemplo:

let minhaPromise = new Promise((resolve, reject) => {
    // aqui nós podemos recuperar algum dado
    if(/* algum dado foi recebido*/) {
        resolve("Promise encerrada");
    }
    else {
        reject("Alguma coisa deu errado :(");
    }
});

minhaPromise.then(result => {
    console.log(result); // "Promise encerrada"
},
erro => {
    console.log(erro); // Error: "Alguma coisa deu errado :("
})

A função resolve, dentro da Promise, indica que o trabalho dela terminou. O método reject serve para tratamento de erros. Temos o uso do método then, que chama a callback passada como parâmetro quando a operação realizada na Promise finalmente termina.

Promises são muito parecidas com callbacks, só que com tratamento de erros. Além disso, são bem mais limpas e fáceis de trabalhar.

No entanto, com o advento da indústria realtime, que trouxe a necessidade de atualizações, análises e dados (últimos dados) em tempo real, veio a necessidade de uma “Promise reutilizável”. Uma que possa ficar sempre “escutando” quando algo finalizar, ou melhor, quando houver uma nova “emissão de dados”. É nesse ponto que surgem os Observables.

Observables

Obervables, como o nome sugere, são observadores. A ideia é muito semelhante a de Promise: esperar que algo seja resolvido e, em resposta, chamar a callback que foi passada para o método then. A callback, ao ser chamada, recebe o que foi resolvido como parâmetro. Só que em vez de then, o método subscribe é usado com Observables. É como se ele observasse se algo acontece e, sempre que acontece, dá uma resposta. Vamos analisar esse exemplo:

// aqui criamos o nosso observable
const meuObservable = Observable.create(observer => {
  observer.next('um');
  observer.next('dois');
  observer.next('três');
  observer.next('quatro');
  observer.complete();
});

// aqui, nos inscrevemos nesse observable
meuObservable.subscribe(
novoValor => console.log(novoValor),
erro => console.log(erro),
() => console.log("fim do observable")
);

// saída:
// "um"
// "dois"
// "três"
// "quatro"
// "fim do observable"

Perceba que o método next faz com que o Observable emita um novo valor, que é capturado pelo subscribe, que chama a sua primeira função callback. O método subscribe realiza o ato de “se inscrever” no observable. Isso quer dizer que ele estará sendo notificado sempre que algo ocorrer com o Observable.

Se algum erro ocorrer, ele será passado para a segunda callback fornecida como parâmetro ao subscribe. Assim, poderemos tratar o erro de uma forma adequada. Se for, por exemplo, um erro temporário de conexão, não precisamos nem mostrar o erro ao usuário, basta a gente se inscrever novamente no Observable, que tentará fazer a conexão novamente.

O método complete indica o fim do observable, que encerra a emissão de novos valores. O subscribe é, então, notificado da finalização e chama a sua terceira função callback. Conseguimos, aqui, fazer um trabalho final depois de que o Observable encerra.

Como visto, Observables trazem a vantagem de identificar atualizações em tempo real e, sempre que há alguma atualização, reagir a ela. Isso de uma farma bastante simples e com tratamento de erros.

É importante entendermos que por trás dos Observables temos o ReactiveX fazendo toda essa “mágica”. Na verdade, o ReactiveX segue o chamado Observer Pattern, em que temos um objeto que mantém uma lista de dependentes (os próprios observables) e os notificam sempre que ocorre uma mudança no estado. Esse Design Pattern foi criado extamente visando uma aplicação que espera atualizações em tempo real.

Mas o que é ReactiveX?

É uma biblioteca muito poderasa, com uma abordagem funcional para lidarmos com eventos. Está se tornando presente nas principais frameworks e na maioria das linguagens. Para cada linguagem, há uma vertente do ReactiveX. Por exemplo, para Javascript, temos o RxJS; para Java, temos o RxJava. Demais explicações sobre cada uma dessas vertentes estão no site oficial. Para dar uma olhada nas linguagens disponíveis, basta acessar esse link.

Além de trazer consigo os Observables, que comentamos acima, disponibiliza também vários métodos e operadores, que nos possibilitam desde uma simples incrição num Observable, como também tarefas avançadas sobre eles. Por exemplo, imagine que você tem um encadeamento de dados assíncronos que precisam uns dos outros, com operadores do ReactiveX você poderá combinar vários observables e obter uma única incrição. Podemos também usar lógicas mais complexas.

Para ter uma ideia da quantidade de operadores e métodos, você pode dar uma olhada na documentação. São realmente muitos, mas não se assuste. Cada um deles tem seu uso e existem exatamente para nos auxiliar quando precisarmos de algo mais específico.

Aprender ReactiveX, no entanto, pode ser um desafio, porque trás uma nova abordagem, com foco na programação declarativa e reativa. Para quem teve somente um contato com a programação imperativa, por exemplo, pode ser difícil assimilar.

Por isso, se você se interessou em aprender mais dessa tecnologia, além da documentação, é interessante que você busque um curso que lhe ensine o essencial sobre o que é mais comum. O suficiente para você começar a usar ReactiveX e tornar suas aplicações mais realtime, uma tendência crescente no mercado. Vale ressaltar que o aprendizado pode ser difícil, mas com certeza vale a pena o esforço!

Ah! E se você gostou e está empolgado para usar ReactiveX com Javascript, nós, da Cod3r, pensando em tornar esse aprendizado mais simples e prático, preparamos um curso de Javascript Funcional que aborda o essencial para você começar a usar RxJS! Confira na Udemy ou na nossa plataforma.

Bons estudos!