Vous êtes sur la page 1sur 41

Angular : RxJS

Achref El Mouelhi

Docteur de l’université d’Aix-Marseille


Chercheur en programmation par contrainte (IA)
Ingénieur en génie logiciel

elmouelhi.achref@gmail.com

H & H: Research and Training 1 / 32


Plan
1 Introduction
2 Observable
3 Observer
4 Opérateurs
5 Combinaison d’opérateurs
6 Opérateurs d’agrégations
7 Fusion d’observables
8 Subject
9 Behavior Subject
10 Replay Subject
11 Async Subject

H & H: Research and Training 2 / 32


Introduction

Angular
Programmation réactive

paradigme de programmation orienté flux de données et


propagation des changements
inspiré du patron de conception observable
H I ©
Deux concepts importants :
U EL
O
f E LM
Observable : une fonction retournant un flux de valeurs à un

ch r e
observateur de manière synchrone ou asynchrone. Un observable
s’exécute s’il y a un observateur (observer) et un abonnement
© A
(avec la méthode subscribe())
Observer : un élément (objet) qui se souscrit (subscribe()) à un
observable pour recevoir les changements et exécuter une suite de
code

H & H: Research and Training 3 / 32


Introduction

Angular
Programmation réactive

paradigme de programmation orienté flux de données et


propagation des changements
inspiré du patron de conception observable
H I ©
Deux concepts importants :
U EL
O
f E LM
Observable : une fonction retournant un flux de valeurs à un

ch r e
observateur de manière synchrone ou asynchrone. Un observable
s’exécute s’il y a un observateur (observer) et un abonnement
© A
(avec la méthode subscribe())
Observer : un élément (objet) qui se souscrit (subscribe()) à un
observable pour recevoir les changements et exécuter une suite de
code

RxJS : Reactive extensions for JavaScript


H & H: Research and Training 3 / 32
Introduction

Angular

La méthode subscribe() prend trois paramètre


1
I ©
la première se déclenche à chaque fois que l’observable émet de
H
EL
nouvelles données (ces données sont reçues comme paramètre)
U
O
2

f E LM
la deuxième se déclenche si l’observable émet une erreur, et

ch r e
reçoit cette erreur comme paramètre
3
©A
la troisième se déclenche lorsque l’observable se termine, et ne
reçoit aucun paramètre.

H & H: Research and Training 4 / 32


Introduction

Angular

Pour commencer
H I ©
EL
OU
créez un composant observable
M
E L /app-observable> dans
ajoutez <app-observable><
f
chr e
app.component.html
© A

H & H: Research and Training 5 / 32


Introduction

Le fichier observable.component.ts
import { Component, OnInit } from ’@angular/core’;

@Component({
selector: ’app-observable’,
templateUrl: ’./observable.component.html’,
styleUrls: [’./observable.component.css’]
})
export class ObservableComponent implements OnInit {
H I ©
status = ’’;
UEL
tab: Array<number> = [];
O
constructor() { }
ngOnInit() { }
f E LM
}
ch r e
©A

H & H: Research and Training 6 / 32


Introduction

Le fichier observable.component.ts
import { Component, OnInit } from ’@angular/core’;

@Component({
selector: ’app-observable’,
templateUrl: ’./observable.component.html’,
styleUrls: [’./observable.component.css’]
})
export class ObservableComponent implements OnInit {
H I ©
status = ’’;
UEL
tab: Array<number> = [];
O
constructor() { }
ngOnInit() { }
f E LM
}
ch r e
©A
Le fichier observable.component.html
<h1>éléments</h1>
<ul>
<li *ngFor="let elt of tab">
{{ elt }}
</li>
</ul>
<div>{{ status }}</div>
H & H: Research and Training 6 / 32
Observable

Angular
Pour créer un observable, on peut utiliser la méthode of() qui convertit un
ensemble de paramètres en observable
import { Component, OnInit } from ’@angular/core’;
import { Observable, of } from ’rxjs’;

@Component({
H I ©
selector: ’app-observable’,
U EL
templateUrl: ’./observable.component.html’,
O
})
f E LM
styleUrls: [’./observable.component.css’]

ch r e
export class ObservableComponent implements OnInit {

status = ’’; ©A
tab: Array<number> = [];
constructor() { }
ngOnInit(): void {
const observable: Observable<number> = of(1, 2, 3);
}
}

H & H: Research and Training 7 / 32


Observable

Angular
On peut utiliser from() pour construire un observable à partir d’un tableau
import { Component, OnInit } from ’@angular/core’;
import { Observable, from } from ’rxjs’;

@Component({
selector: ’app-observable’,
H I ©
templateUrl: ’./observable.component.html’,
U EL
styleUrls: [’./observable.component.css’]
O
})

f E LM
export class ObservableComponent implements OnInit {

ch r e
©A
status = ’’;
tab: Array<number> = [];
constructor() { }
ngOnInit() {
const tableau = [1, 2, 3];
const observable: Observable<number> = from(tableau);
}
}

H & H: Research and Training 8 / 32


Observable

Angular
On peut utiliser range() pour définir un interval de nombre entier

import { Component, OnInit } from ’@angular/core’;


import { Observable, range } from ’rxjs’;

@Component({
selector: ’app-observable’,
H I ©
templateUrl: ’./observable.component.html’,
U EL
styleUrls: [’./observable.component.css’]
O
})
f E LM
ch r e
export class ObservableComponent implements OnInit {

status = ’’;
©A
tab: Array<number> = [];
constructor() { }
ngOnInit() {
const observable: Observable<number> = range(1, 3);
}
}

H & H: Research and Training 9 / 32


Observer

Angular
Lorsqu’un observateur s’abonne à notre observable, il peut implémenter trois
méthodes pour spécifier ce qu’il faut faire

ngOnInit() {
const tableau = [1, 2, 3];
const observable: Observable<number> = from(tableau);
const observer: Observer<number> = {
H I ©
next: (value) => {
U EL
this.tab.push(value);
O
},
f E LM
error: (error) => {
this.status = error;
ch r e
},
complete: ©A
() => {
this.status = ’fini’;
}
};

H & H: Research and Training 10 / 32


Observer

Angular
On peut aussi directement faire
ngOnInit() {
const tableau = [1, 2, 3];
const observable: Observable<number> = from(tableau);
observable.subscribe(
(value) => {
H I ©
},
this.tab.push(value);
U EL
O
LM
(error) => {
this.status = error;
r e f E
ch
},

©A
() => {
this.status = ’fini’;
}
);
}

L’émission de valeurs s’effectue d’une manière synchrone et par conséquent on ne


voit pas la réception des éléments dans le navigateur.

H & H: Research and Training 11 / 32


Observer

Angular
Pour avoir une exécution asynchrone, on utilise la fonction interval(1000)
une infinité de valeurs incrémentielles commençant de 0 : une valeur par
seconde

ngOnInit() {

I ©
const observable: Observable<number> = interval(1000);
H
observable.subscribe(
(value) => {
UEL
O
LM
this.tab.push(value);
},
(error) => {
r e f E
ch
©A
this.status = error;
},
() => {
this.status = ’fini’;
}
);
}

H & H: Research and Training 12 / 32


Observer

Pour avoir une exécution asynchrone, on utilise la fonction timer(3000,


1000) qui envoie une infinité de valeurs incrémentielles commençant de 0 : une
valeur par seconde la première valeur sera envoyée après 3 secondes

ngOnInit() {
const observable: Observable<number> = timer(3000, 1000);
observable.subscribe(
(value) => {
H I ©
EL
this.tab.push(value);
},
O U
LM
(error) => {

},
this.status = error;

r e f E
ch
©A
() => {
this.status = ’fini’;
}
);
}

Les deux fonctions timer() et interval() ne se terminent jamais.

H & H: Research and Training 13 / 32


Observer

Pour éviter les problèmes de mémoire, il faut penser à se désabonner à la destruction du


composant

import { Component, OnInit, OnDestroy } from ’@angular/core’;


import { Observable, Subscription, timer } from ’rxjs’;
@Component({
selector: ’app-observable’,
templateUrl: ’./observable.component.html’,
styleUrls: [’./observable.component.css’]
})
H I ©
EL
export class ObservableComponent implements OnInit, OnDestroy {
tab: Array<number> = [];
O U
LM
status = ’’;
constructor() { }
subscription: Subscription;
r e f E
ch
ngOnInit() {

©A
const observable: Observable<number> = timer(3000, 1000);
this.subscription = observable.subscribe(
(value) => { this.tab.push(value); },
(error) => { this.status = error; },
() => { this.status = ’fini’; }
);
}
ngOnDestroy() { this.subscription.unsubscribe(); }
}

H & H: Research and Training 14 / 32


Opérateurs

Angular
Pour indiquer le nombre d’élément à émettre, on utilise la méthode pipe() qui nous
permet de faire appel à l’opérateur take()

ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(take(10));

observable.subscribe(
(value) => {
H I ©
this.tab.push(value);
U EL
},
O
LM
(error) => {

},
this.status = error;

r e f E
() => {
ch
);
} ©A
this.status = ’fini’;

On peut importer les opérateurs ainsi : import { take } from ’rxjs/operators’;

H & H: Research and Training 15 / 32


Combinaison d’opérateurs

Angular
On peut combiner les opérateurs en appliquant une modification sur les 10
éléments reçus
ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(
take(10),
map(elt => elt + 3)
H I ©
EL
);

O U
LM
observable.subscribe(
(value) => {
this.tab.push(value);
r e f E
ch
©A
},
(error) => {
this.status = error;
},
() => {
this.status = ’fini’;
}
);
}
H & H: Research and Training 16 / 32
Combinaison d’opérateurs

Angular
On peut aussi filtrer les éléments pairs
ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(
take(10),
map(elt => elt + 3),
filter(elt => elt % 2 === 0)
H I ©
EL
);

O U
LM
observable.subscribe(
(value) => {
this.tab.push(value);
r e f E
ch
©A
},
(error) => {
this.status = error;
},
() => {
this.status = ’fini’;
}
);
}
H & H: Research and Training 17 / 32
Opérateurs d’agrégations

Angular
On peut aussi compter les éléments selon un critère
ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(
take(10),
count(elt => elt % 2 === 0)
);
H I ©
observable.subscribe(
UEL
O
LM
(value) => {

},
this.tab.push(value);
r e f E
ch
©A
(error) => {
this.status = error;
},
() => {
this.status = ’fini’;
}
);
}

H & H: Research and Training 18 / 32


Opérateurs d’agrégations

Angular
On peut sélectionner le maximum
ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(
take(10),
max()
);
H I ©
observable.subscribe(
UEL
O
LM
(value) => {

},
this.tab.push(value);
r e f E
ch
©A
(error) => {
this.status = error;
},
() => {
this.status = ’fini’;
}
);
}

H & H: Research and Training 19 / 32


Fusion d’observables

Angular
On peut fusionner plusieurs observables grâce à la fonction merge()
ngOnInit() {
const tableau = [1, 2, 3];
const observable1: Observable<number> = of(1, 2, 3);
const observable2: Observable<number> = of(4, 5, 6);
const merged = merge(
observable1,
H I ©
);
observable2

U EL
O
merged.subscribe(
f E LM
(value) => {

ch
this.tab.push(value); r e
©A
},
(error) => {
this.status = error;
},
() => {
this.status = ’fini’;
}
);
}

H & H: Research and Training 20 / 32


Subject

Angular

Subject H I ©
U EL
O
Il a à la fois le rôle d’un observateur et d’un observable

f E LM
ch r e
Il autorise la souscription de plusieurs observateurs

©A

H & H: Research and Training 21 / 32


Subject

Angular
Dans ngOnInit(), commençons par déclarer un Subject
const subject = new Subject<number>();

H I ©
U EL
O
f E LM
ch r e
©A

H & H: Research and Training 22 / 32


Subject

Angular
Dans ngOnInit(), commençons par déclarer un Subject
const subject = new Subject<number>();

Plusieurs observateurs peuvent s’abonner à notre subject


subject.subscribe({
H I ©
EL
next: (value) => console.log(‘A : ${value}‘)
});
O U
LM
subject.subscribe({
next: (value) => console.log(‘B : ${value}‘)
});
r e f E
ch
©A

H & H: Research and Training 22 / 32


Subject

Angular
Dans ngOnInit(), commençons par déclarer un Subject
const subject = new Subject<number>();

Plusieurs observateurs peuvent s’abonner à notre subject


subject.subscribe({
H I ©
EL
next: (value) => console.log(‘A : ${value}‘)
});
O U
LM
subject.subscribe({
next: (value) => console.log(‘B : ${value}‘)
});
r e f E
ch
©A
Introduisons deux nouvelles valeurs à notre subject
subject.next(1);
subject.next(2);
// le résultat est
// A : 1
// B : 1
// A : 2
// B : 2
H & H: Research and Training 22 / 32
Subject

Un Subject est aussi un observateur, il peut donc s’abonner à un observable

ngOnInit() {
const subject = new Subject<number>();
subject.subscribe({
next: (value) => console.log(‘A : ${value}‘)
});
subject.subscribe({
next: (value) => console.log(‘B : ${value}‘)
});
H I ©
U EL
O
LM
observable.subscribe(subject);
}

r e f E
ch
©A

H & H: Research and Training 23 / 32


Subject

Un Subject est aussi un observateur, il peut donc s’abonner à un observable

ngOnInit() {
const subject = new Subject<number>();
subject.subscribe({
next: (value) => console.log(‘A : ${value}‘)
});
subject.subscribe({
next: (value) => console.log(‘B : ${value}‘)
});
H I ©
U EL
O
LM
observable.subscribe(subject);
}

r e f E
ch
Le résultat est ©A
// A : 1
// B : 1
// A : 2
// B : 2
// A : 3
// B : 3

H & H: Research and Training 23 / 32


Subject

Angular

Remarque

H I ©
EL
Un observable ne peut avoir qu’un seul observateur

O U
LM
Un observable peut utiliser l’opérateur multicast et un

r e f E
subject pour avoir plusieurs observateurs

A ch ainsi un ConnectableObservable et
L’observable devient
utilise la©
méthode connect pour propager les changements

H & H: Research and Training 24 / 32


Subject

Exemple
const observable: Observable<number> = from([1, 2, 3]);
const subject = new Subject<number>();

const multicasted = observable.pipe(multicast(subject)) as


ConnectableObservable<number>;

multicasted.subscribe({
next: (value) => console.log(‘A : ${value}‘)
H I ©
EL
});
multicasted.subscribe({
O U
next: (value) => console.log(‘B : ${value}‘)
});
multicasted.connect();
f E LM
ch r e
©A

H & H: Research and Training 25 / 32


Subject

Exemple
const observable: Observable<number> = from([1, 2, 3]);
const subject = new Subject<number>();

const multicasted = observable.pipe(multicast(subject)) as


ConnectableObservable<number>;

multicasted.subscribe({
next: (value) => console.log(‘A : ${value}‘)
H I ©
EL
});
multicasted.subscribe({
O U
next: (value) => console.log(‘B : ${value}‘)
});
multicasted.connect();
f E LM
ch r e
Le résultat est ©A
// A : 1
// B : 1
// A : 2
// B : 2
// A : 3
// B : 3

H & H: Research and Training 25 / 32


Subject

Angular
Pour se désabonner, on appelle la méthode unsubscribe() depuis la souscription
ngOnInit() {
const observable: Observable<number> = interval(1000).pipe(take(10)
);

const subject = new Subject<number>();

H I ©
EL
const multicasted = observable.pipe(multicast(subject)) as
ConnectableObservable<number>;
O U
const a = multicasted.subscribe({
f E LM
});
ch r e
next: (value) => console.log(‘A : ${value}‘)

©A
const b = multicasted.subscribe({
next: (value) => console.log(‘B : ${value}‘)
});

multicasted.connect();

setTimeout(() => a.unsubscribe(), 3000);


setTimeout(() => b.unsubscribe(), 5000);
}
H & H: Research and Training 26 / 32
Behavior Subject

Angular

Behavior Subject

H I ©
Une des variantes de Subject fonctionnant avec la notion de

EL
valeur actuelle

O U
LM
Il stocke la dernière valeur émise par ses observateurs

r e f E
Chaque fois qu’un nouvel observateur s’abonne, il reçoit
A ch
immédiatement la valeur actuelle
©
Le constructeur de la classe BehaviorSubject prend comme
paramètre la valeur initiale

H & H: Research and Training 27 / 32


Behavior Subject

Angular
Exemple
ngOnInit() {
const subject = new BehaviorSubject(0);
subject.subscribe({
next: (value) => console.log(‘A : ${value}‘)
});
subject.next(1);
H I ©
EL
subject.next(2);
subject.subscribe({
O U
LM
next: (value) => console.log(‘B : ${value}‘)
});
subject.next(3);
r e f E
ch
}

©A

H & H: Research and Training 28 / 32


Behavior Subject

Angular
Exemple
ngOnInit() {
const subject = new BehaviorSubject(0);
subject.subscribe({
next: (value) => console.log(‘A : ${value}‘)
});
subject.next(1);
H I ©
EL
subject.next(2);
subject.subscribe({
O U
LM
next: (value) => console.log(‘B : ${value}‘)
});
subject.next(3);
r e f E
ch
}

Le résultat est
©A
// A : 0
// A : 1
// A : 2
// B : 2
// A : 3
// B : 3
H & H: Research and Training 28 / 32
Replay Subject

Angular

ReplaySubject

H I ©
Une des variantes de Subject fonctionnant avec la notion de
EL
nombre de valeurs à conserver dans l’historique
U
M O
f E L dansabonn
Il conserve un nombre de valeurs l’historique afin qu’elles

c h r e
puissent être envoyées aux nouveaux és.

© A de la classe ReplaySubject prend comme


Le constructeur
paramètre le nombre de valeurs à conserver dans l’historique

H & H: Research and Training 29 / 32


Replay Subject

Exemple
ngOnInit() {
const subject = new ReplaySubject<number>(2);
subject.next(0);
subject.subscribe({
next: (value) => console.log(‘A : ${value}‘)
});
subject.next(1);
subject.next(2);
H I ©
EL
subject.subscribe({
next: (value) => console.log(‘B : ${value}‘)
});
O U
}
subject.next(3);

f E LM
ch r e
©A

H & H: Research and Training 30 / 32


Replay Subject

Exemple
ngOnInit() {
const subject = new ReplaySubject<number>(2);
subject.next(0);
subject.subscribe({
next: (value) => console.log(‘A : ${value}‘)
});
subject.next(1);
subject.next(2);
H I ©
EL
subject.subscribe({
next: (value) => console.log(‘B : ${value}‘)
});
O U
}
subject.next(3);

f E LM
ch r e
Le résultat est ©A
// A : 0
// A : 1
// A : 2
// B : 1
// B : 2
// A : 3
// B : 3
H & H: Research and Training 30 / 32
Async Subject

Angular

Async Subject
H I ©
Une des variantes de Subject
UEL
O
f E LM
Il envoie seulement la dernière valeur à ses observateurs et c’est

ch r e
lorsqu’il finit quel que soit l’ordre de leur abonnement

©A
Pour signaler sa fin, il appelle la méthode complete

H & H: Research and Training 31 / 32


Async Subject

Angular
Exemple
ngOnInit() {
const subject = new AsyncSubject();

subject.next(0);
subject.subscribe({
next: (value) => console.log(‘A : ${value}‘)
H I ©
EL
});
subject.next(1);
O U
LM
subject.next(2);
subject.subscribe({

e f E
next: (value) => console.log(‘B : ${value}‘)
r
ch
});

©A
subject.next(3);

subject.complete();
}

H & H: Research and Training 32 / 32


Async Subject

Angular
Exemple
ngOnInit() {
const subject = new AsyncSubject();

subject.next(0);
subject.subscribe({
next: (value) => console.log(‘A : ${value}‘)
H I ©
EL
});
subject.next(1);
O U
LM
subject.next(2);
subject.subscribe({

e f E
next: (value) => console.log(‘B : ${value}‘)
r
ch
});

©A
subject.next(3);

subject.complete();
}

Le résultat est
// A : 3
// B : 3

H & H: Research and Training 32 / 32

Vous aimerez peut-être aussi