web-dev-qa-db-fra.com

RxJS abonnements multiples à Observable?

Je crée mon propre observable et souscrit à deux fonctions. Je m'attendrais à avoir les deux fonctions exécutées pour chaque élément de la séquence mais seulement le dernier l'est. 

let observer = null
const notificationArrayStream = Rx.Observable.create(function (obs) {
  observer = obs;
  return () => {}
})

function trigger(something) {
  observer.next(something)
}

notificationArrayStream.subscribe((x) => console.log('a: ' + x))
notificationArrayStream.subscribe((x) => console.log('b: ' + x))

trigger('TEST')

Production attendue

a: TEST
b: TEST

Sortie réelle

b: TEST

Voici le JSBin: http://jsbin.com/cahoyey/edit?js,console

Pourquoi donc? Comment puis-je avoir plusieurs fonctions abonnées à un seul observable?

27
cgross

Pour que plusieurs fonctions s'abonnent à un seul observable, il suffit de les souscrire à cet observable, c'est aussi simple que cela. Et en fait c'est ce que tu as fait.

MAIS votre code ne fonctionne pas car après l'exécution de notificationArrayStream.subscribe((x) => console.log('b: ' + x)), observer est (x) => console.log('b: ' + x)), donc observer.next vous donnera b: TEST.

Donc, fondamentalement, c'est votre création observable qui est fausse. Dans create, vous avez passé un observateur en tant que paramètre pour pouvoir lui transmettre des valeurs. Ces valeurs que vous devez générer d’une manière ou d’une autre par votre propre logique, mais comme vous pouvez le constater, votre logique est erronée. Je vous recommanderais d'utiliser un sujet si vous souhaitez transmettre des valeurs à l'observateur.

Quelque chose comme: 

const notificationArrayStream = Rx.Observable.create(function (obs) {
  mySubject.subscribe(obs);
  return () => {}
})

function trigger(something) {
  mySubject.next(something)
}
16
user3743222

Subject

Dans votre cas, vous pouvez simplement utiliser un Subject. Un subject vous permet de partager une exécution unique avec plusieurs observateurs lorsqu’elle l’utilise comme proxy pour un groupe d’abonnés et une source.

En substance, voici votre exemple utilisant un sujet:

const subject = new Subject();

function trigger(something) {
    subject.next(something);
}

subject.subscribe((x) => console.log('a: ' + x));
subject.subscribe((x) => console.log('b: ' + x));

trigger('TEST');

Résultat:

a: TEST
b: TEST

Piège: les observateurs arrivent trop tard

Notez que le moment auquel vous vous abonnez et la diffusion des données est pertinent. Si vous envoyez une diffusion avant de vous abonner, cette diffusion ne vous avertit pas:

function trigger(something) {
    subject.next(something);
}

trigger('TEST');

subject.subscribe((x) => console.log('a: ' + x));
subject.subscribe((x) => console.log('b: ' + x));

Résultat: (vide)


ReplaySubject & BehaviorSubject

Si vous voulez vous assurer que même les futurs abonnés soient avertis, vous pouvez utiliser un ReplaySubject ou un BehaviorSubject .

Voici un exemple utilisant ReplaySubject (avec une taille de cache de 5, ce qui signifie que jusqu'à 5 valeurs du passé seront mémorisées, par opposition à un BehaviorSubject qui ne peut retenir que la dernière valeur):

const subject = new ReplaySubject(5); // buffer size is 5

function trigger(something) {
    subject.next(something);
}

trigger('TEST');

subject.subscribe((x) => console.log('a: ' + x));
subject.subscribe((x) => console.log('b: ' + x));

Résultat:

a: TEST
b: TEST
42
Mobiletainment

Chaque fois que vous vous abonnez, vous écrasez varobservateur.

La fonction trigger ne fait référence qu'à celle-ci var, donc aucune surprise s'il n'y a qu'un seul journal.

Si nous faisons de la var un tableau, il fonctionne comme prévu:JS Bin

let obs = [];

let foo = Rx.Observable.create(function (observer) {
  obs.Push(observer);
});

function trigger(sth){
//   console.log('trigger fn');
  obs.forEach(ob => ob.next(sth));
}

foo.subscribe(function (x) {
  console.log(`a:${x}`);
});
foo.subscribe(function (y) {
  console.log(`b:${y}`);
});

trigger(1);
trigger(2);
trigger(3);
trigger(4);

Une solution plus propre consisterait à utiliser Subject , comme suggéré ci-dessus.

3
Luca Bertolasi

Vous pouvez construire la classe wrapper Subscribable <> en fonction de ReplaySubject. Ce serait plus propre que de gérer Sujet et Observable:

export class Subscribable<T> {

    private valueSource: Subject = new ReplaySubject(1);
    public value: Observable;
    private _value: T;

    constructor() {
        this.value = this.valueSource.asObservable();
    }

    public set(val: T) {
        this.valueSource.next(val);
        this._value = val;
    }

    public get(): T {
        return this._value;
    }
}

Usage:

let arrayStream : Subscribable<TYPE> = new Subscribable<TYPE>();

…
public setArrayStream (value: TYPE) {
    this.set(value);
}

Gérer le changement de valeur:

arrayStream.value.subscribe(res => { /*handle it*/ });

Article original: http://devinstance.net/articles/20170921/rxjs-subscribable

0
yfranz

Au lieu d'utiliser un sujet, il est également possible d'utiliser le combo publishReplay () + refCount () pour permettre à un observable de multidiffuser à plusieurs abonnés:

const notificationArrayStream = Rx.Observable.create(function (obs) {
  observer = obs;
  return () => {}
}).pipe(publishReplay(), refCount())
0
golfadas