Já entendemos em outro artigo o que são promises e também conhecemos melhor o padrão observer. Com base nisso podemos entender melhor agora as diferenças entre eles. Então vamos logo entender!
Métodos
Em Promises podemos envolver (encapsular) dados e tratar eles com os operadores .then() e catch(). O mesmo ocorre com as Observables, onde temos next(), error(), e complete():
const { Observable } = require('rxjs')
const promise = new Promise(resolve => {
resolve('Promise é bem legal!')
})
promise.then(console.log);
promise.catch();
const obs = new Observable(subscriber => {
subscriber.next('Sua primeira Observer')
})
obs.subscribe({
next() {
},
complete() {
},
error()
})
Lembrando que o next() utilizamos quando há uma nova emissão, error() quando temos que tratar um possível erro e o complete() indica quando o Observable é completado.
Múltiplos Valores VS Único Valor
Uma diferença notável é que com observables podemos receber mais de um valor, ou seja, múltiplos dados. Já as promises apenas nos retornam um único dado. Vamos ver isso na prática, veja o exemplo:
const { Observable } = require('rxjs')
const promise = new Promise(resolve => {
resolve('Sua primeira Promise!')
resolve('Sua segunda Promise!')
})
promise.then(console.log)
promise.then(text => console.log(text.toUpperCase()))
const obs = new Observable(subscriber => {
setTimeout(() => {
subscriber.next('Sua')
subscriber.next('primeira')
subscriber.next('Observer')
subscriber.next('!')
}, 2000);
setTimeout(() => {
subscriber.next('Sua')
subscriber.next('segunda')
subscriber.next('Observer')
subscriber.next('!')
}, 4000);
})
obs.subscribe(console.log)
obs.subscribe(text => console.log(text.toUpperCase()))
O que o código acima vai fazer é exibir as mensagens conforme foi passado para o console.log e depois vamos converter todos os caracteres para letras maiúsculas. Mas preste atenção no seguinte. Na Promise estamos passando dois resolve, com duas mensagens diferentes. Será que teremos as duas mensagens sendo exibidas em letras minúsculas e maiúsculas? Se você testar terá o seguinte resultado:
Fica claro que o comportamento foi bem diferente, como já era de se esperar as Promises nos retornam apenas um valor. Quando colocamos:
resolve('Sua segunda Promise!')
Essa chamada acima não ocorreu, pois a promise já tinha sido resolvida, já que é possível retornar apenas um único tipo de dado. Mas com as Observables já é bem diferente. Podemos retornar vários tipos de dados, e conforme foi possível ver, temos o retorno em letras minúsculas e maiúsculas das duas mensagens! Isso é o que significa emitir múltiplos valores. E isso é uma das vantagens das Observables.
Lazy VS Eager
Quando dizemos que as promises tem um comportamento eager, estamos dizendo que elas tem a sua execução imediata, ou seja, independente de elas terem ou não .then ou catch para serem consumidas, elas sempre serão iniciadas. Diferente das Observables que tem um comportamento lazy, onde as execuções são por demanda. Vamos ver isso na prática:
const { Observable } = require('rxjs')
const promise = new Promise(resolve => {
console.log('Iniciar Promise');
resolve('Sua primeira Promise!')
})
// promise.then(console.log)
// promise.then(text => console.log(text.toUpperCase()))
const obs = new Observable(subscriber => {
console.log('Iniciar Observable');
subscriber.next('Sua')
subscriber.next('primeira')
subscriber.next('Observer')
subscriber.next('!')
subscriber.next('Sua')
subscriber.next('segunda')
subscriber.next('Observer')
subscriber.next('!')
})
// obs.subscribe(console.log)
Podemos observar que temos comentado os métodos que são usados para consumir os dados, para promise o .then e para Observables o .subscriber. E no que isso realmente resulta? Veja na imagem abaixo:
Veja que apenas a promise foi iniciada, mas não foi resolvida. A observable nem mesmo foi inicializada, ou seja, aquele bloco de código nem foi carregado. E isso acontece porque ele só vai executar a Observable quando alguém estiver inscrito, ou escutando aquele bloco de código.
Multicast e Unicast
Este é um conceito bem interessante. As Promises são compartilhadas, multicast. Enquanto que as Observables podem ter um comportamento de compartilhamento ou não. Para entender bem esse conceito temos um exemplo que vai deixar isso claro:
const { Observable } = require('rxjs')
const promise = new Promise(resolve => {
console.log('Iniciar Promise');
setTimeout(() => {
resolve(1)
}, 3000);
})
const obs = new Observable(subscriber => {
console.log('Iniciar Observable');
setTimeout(() => {
subscriber.next(2)
}, 3000);
})
promise.then(a => console.log('Valor da Promise ', a))
obs.subscribe(a => console.log('Valor da Observable ', a))
setTimeout(() => {
promise.then(a => console.log('Novo .then da Promise ', a))
obs.subscribe(a => console.log('Novo subscriber da Observable ', a))
}, 1000);
Se quiser entender o comportamento é importante executar esse código e ver como cada dado vai ser emitido. Por isso acompanhe a execução do código no GIF abaixo:
Notou o comportamento do código? Tivemos apenas uma emissão do console.log do iniciar Promise e o valor 1 que foi passado para ela foi executado de uma vez só para os dois .then() . Mas o que isso significa? Isso demonstra o comportamento compartilhado da promise que apenas emite o valor uma vez, independente se tiver mais de um .then(). Isso prova que a promise só pode ser resolvida uma vez e que nunca volta ao bloco de código para buscar novas informações.
Mas a Observable agiu diferente. Já que tínhamos dois subscribe(), o que ocorreu foi que o bloco de código foi percorrido duas vezes. No primeiro subscribe() e também no segundo. Por isso vimos duas vezes a mensagem iniciar Observable. A observable não compartilhou os dados de uma vez, pois por padrão não é objetivo dele fazer isso. Como existia um outro inscrito interessado no evento, a Observable foi iniciada novamente. Então o valor nunca é emitido imediatamente para os dois inscritos.
É possível sim ter esse mesmo comportamento da Promise em uma Observable, mas para isso é preciso utilizar um método do RxJS chamado share, e o código ficaria dessa maneira:
const { Observable } = require('rxjs')
const { share } = require('rxjs/operators')
const promise = new Promise(resolve => {
console.log('Iniciar Promise');
setTimeout(() => {
resolve(1)
resolve(2)
}, 3000);
})
const obs = new Observable(subscriber => {
console.log('Iniciar Observable');
setTimeout(() => {
subscriber.next(2)
}, 3000);
}).pipe(share()
)
promise.then(a => console.log('Valor da Promise ', a))
obs.subscribe(a => console.log('Valor da Observable ', a))
setTimeout(() => {
promise.then(a => console.log('Novo .then da Promise ', a))
obs.subscribe(a => console.log('Novo subscriber da Observable ', a))
}, 2000);
Se você executar este código verá o mesmo comportamento dos dois lados.
Cancelamento
Não é possível cancelar requisições de promises nativas do JavaScript. Mas quando trabalhamos com Observables isso é possível. Vamos entender melhor isso com mais um exemplo em código:
const { Observable } = require('rxjs')
const obs = new Observable(obs => {
let i = 0
const intervalo = setInterval(() =>
obs.next(i++)
, 1500);
return () => clearInterval(intervalo)
})
const inscrito = obs.subscribe(a => console.log('Valor da Observable ', a))
setTimeout(() => {
inscrito.unsubscribe();
}, 7000);
Vai perceber que ao rodar esse código a observable em certo momento vai parar de ser executada. Pois estamos retirando a inscrição que fizemos anteriormente, utilizando do unsubscribe:
Isso nos mostra a grande vantagem de se trabalhar com elas. Temos mais poder de lidar com nosso código em certos momentos.
Espero que tudo o que foi abordado tenha deixado claro as principais diferenças.
E além de tudo isso que vimos, podemos destacar que as promises sempre são assíncronas enquanto as observables podem ser síncronas ou assíncronas. Para que a leitura não fique maior do que já está, é bom abordar este tópico em um outro artigo.
Conclusão
Entender essas diferenças nos ajuda a ter uma boa visão sobre qual vamos utilizar. Além disso podemos ter um código inteligente e organizado. Não basta apenas utilizar os objetos Promises e Observables sem saber exatamente o que eles fazem. É preciso sim estudar e entender suas diferenças, vantagens e desvantagens. Por isso sempre pratique o que está estudando colocando a mão na massa. Lembrando que as Promises são nativas do JavaScript, enquanto as Observables são da biblioteca RxJS.
Espero que este artigo tenha deixado claro as diferenças. Fico por aqui neste post, agradeço por ler. Se tiver alguma dúvida deixe nos comentários ou pode entrar em contato pelo meu LinkedIn . Muito obrigado e até o próximo post. 😉