81 RX – Cos’è un Observable

Gli Observable sono delle collezioni di strumenti che permettono di “osservare” dei dati o meglio restare in attesa in modo passivo / pigro (lazy), di molteplici valori.

In JavaScript le funzioni standard adottano un sistema di tipo pull, nel senso che quando chiamiamo una funzione e questa ci ritorna un singolo valore sarà una parte del nostro codice che decide quando prelevare questo valore invocando tale funzione. Nel caso di sistema push, come nelle promise, è il contrario è la restituzione di un valore che aziona il flusso del codice e ci saranno degli osservatori (observers) riceveranno questi valori quando l’observable deciderà di consegnare questi valori.

Sia le promise che le funzioni restituiscono un singolo valore, mentre un observable può restituire diversi valori in un determinato periodo di tempo.

Entriamo nel pratico creando una directory OBSERVABLES e creiamo al suo interno un file index.js e importiamo la classe Observable utilizzando node.js

const { Observable } = require('rxjs');

abbiamo importato 1 solo elemento, l’ Observable, dalla libreria RxJS ora istanziamo un nuovo oggetto di classe Observable

const objs = new Observable();

Questo Observable si aspetta passata una funzione alla quale verrà a sua volta passato observer ovvero la funzione o oggetto che andrà ad osservare questi valori. Utilizzando una arrow function creo il parametro subcriber (colui che si iscrive) che fondamentalmente potrà chiamare 3 metodi:

  1. next con questo metodo passiamo il prossimo valore
    const objs = new Observable( subscriber =>{
        subscriber.next(1);
        subscriber.next(2);
    });

    in questo caso ho passato come esempio i numeri 1 e poi 2

  2. error è un metodo per osservare e ricevere eventuali errori
  3. complete è il metodo che completa la funzione
    const objs = new Observable( subscriber =>{
        subscriber.next(1);
        subscriber.next(2);
        subscriber.complete();
    });

Se noi lanciamo il codice non verrà prodotto alcun risultato perché come abbiamo detto all’inizio, l’observable è un metodo push pigro e fintanto che non verrà invocato, non emetterà i suoi valori. Quindi nel nostro caso, finché non ci sarà un subscriber  che effettuerà la subscribe dell’oggetto i valori non saranno emessi

objs.subscribe();

ora passiamo come parametro della subscribe una funzione.

La funzione come argomento della subscribe riceve i valori dal next e potrà effettuare operazioni, nel nostro esempio manderemo i valori nella console

objs.subscribe(v =>{
console.log(v);
});

lanciando a terminale

node index.js

verranno stampati il nostri valori 1 e 2.

La domanda che viene lecita è come facciamo a sapere quando l’observable ha finito di processare i valori?

Come abbiamo precedentemente visto esistono 3 metodi che la subscribe accetta, pertanto il secondo parametro che possiamo passare è l’errore, quindi error

objs.subscribe(v =>{
    console.log(v);
}, error => {console.log(error)});

a questo punto se si generasse un errore, sarebbe restituito.

Come ultimo parametro accettato dal subscriber c’è il complete, lo mettiamo con solo le tonde ( ) senza nome della funzione , perché non deve ricevere nessun parametro in ingresso e facciamo restituire un messaggio di operazione completata

objs.subscribe(
    v =>{
    console.log(v);
}
,error => {
        console.log(error)
    }, () => {
        console.log('Completato!');
    }
);

Una volta che abbiamo inserito il complete nell’observable, qualsiasi valore inseriamo dopo non sarà considerato

const objs = new Observable( subscriber =>{
    subscriber.next(1);
    subscriber.next(2);
    subscriber.complete();
    subscriber.next(4);
});

Il valore 4 non verrà stampato perché inserito dopo il metodo complete.

Passare un oggetto come parametro

Questo sistema di passare una funzione come parametro all’ observable è sicuramente quello principale e più utilizzato, in realtà possiamo passare la funzione anche sottoforma di oggetto.

Un’ altra caratteristica degli observable è quella che se mettessimo un altro subscribe, questo riceverà i medesimi valori dall’ observable, come se fosse uno nuovo. Ogni istanza viene considerata a se stante, ignorando le altre, ricevendo gli stessi valori. Aggiungiamo quindi un altra subscribe passando un oggetto come parametro invece che una funzione. Questo oggetto avrà una proprietà next che avrà come valore una funzione che riceverà a sua volta il valore

objs.subscribe({
    next : v => {
        console.log('sottoscrizion2 = '+v)
    }
});

Eseguendo il codice si noterà che saranno eseguite entrambe le subscribe indipendentemente,

1
2
Completato!
sottoscrizion2 = 1
sottoscrizion2 = 2

Ora vediamo come passare il completed all’oggetto:

objs.subscribe({
    next : v => {
        console.log('sottoscrizion2 = '+v)
    },
    complete : () => {console.log('Seconda sottoscrizione completata')}
});

Se osserviamo il completed viene dichiarato come proprietà dell’oggetto, inoltre non ricevendo valori in ingresso abbiamo utilizzato un funzione freccia.

In modo del tutto analogo posso gestire anche la proprietà error

objs.subscribe({
    next : v => {
        console.log('sottoscrizion2 = '+v)
    },
    complete : () => {console.log('Seconda sottoscrizione completata')},
    error : error => console.log(error)
});

Questo è il modo in cui si passa un oggetto ad un’ observable, di norma se ci interessa il valore si passa una funzione che più semplice di un oggetto.

Gli observable gestiscono valori sincroni e asincroni

Come abbiamo visto i dati dalla subscribe vengono restituiti in sequenza dal primo all’ultimo, con una differenza di tempo di esecuzione. Se noi aggiungessimo un altro comando next che venga eseguito dopo alcuni secondi, utilizzando per esempio la funzione predefinita setTimeout(), prima della completed:

const objs = new Observable( subscriber =>{
    subscriber.next(1);
    subscriber.next(2);
    setTimeout(()=> {subscriber.next(4);}, 4000);
    subscriber.complete();
});

non otterremmo il valore 4, perché mentre il codice attende i 4000 millisecondi per eseguire la subscribe, esegue anche il restante codice e quindi esegue il completed che interrompe la subscribe.

Commentando il completed però il codice verrà eseguito fino al setTimeout, appena lo incontra esegue il restante codice e  i restanti subscribe e dopo 4 secondi restituisce sia il valore 4 che anche sottoscrizion2 = 4

1
2
sottoscrizion2 = 1
sottoscrizion2 = 2
4
sottoscrizion2 = 4

Questo ci permette di capire che le funzioni degli observable come la subscribe lavorano oltre che con valori sincroni anche in modo asincrono

Riassuntino

Observable è l’oggetto principale della libreria rxjs, per crearlo dobbiamo stanziare questo observable, passare una funzione (anonima) che riceverà come argomento il subscriber e ogni volta che effettueremo un subscribe la funzione verrà eseguita, nel nostro esempio vengono chiamati i vari next (1 e 2), anche quelli asincroni (immaginate delle chiamate AJAX che effettuano un matchable da API esterne).

Possiamo chiamare più subscribe i quali lavorano come fossero rami separati e ricevono i valori indipendentemente. I next chiamati dopo la complete verranno ignorati.

Inoltre alla subscribe possiamo passare un oggetto dove next, completed ed error sono passati come proprietà.