web-dev-qa-db-fra.com

Comment créer Observable à partir de la fonction?

Je veux appeler une fonction (de manière synchrone) puis utiliser sa valeur de retour comme émission initiale (enchaîner ensuite d'autres opérateurs sur le résultat observable).

Je veux invoquer cette fonction pendant l'abonnement, donc je ne peux pas simplement utiliser Observable.of(() => getSomeValue()). J'ai vu bindCallback (auparavant fromCallback) mais je ne pense pas qu'il puisse être utilisé pour cette tâche (corrigez-moi si je me trompe). J'ai vu start opérateur statique dans la documentation v4 mais apparemment il n'est pas implémenté dans la v5 (et aucune indication que c'est sur le chemin). RxJava a également l'opérateur fromCallable qui fait exactement cela afaik.

La seule façon dont je pouvais penser est comme ceci:

Observable.create((observer: Observer<void>) => {
  let val = getSomeValue();
  observer.next(val);
  observer.complete();
})

ce qui, je pense, fait exactement cela. Mais cela semble tellement compliqué pour une chose simple qui aurait probablement dû être comme Observable.fromFunction(() => getSomeValue()) Et si je veux l'exécuter de manière asynchrone, comme le fait l'opérateur start? Comment puis-je faire cela dans la version actuelle de RxJS?

17
Titan

J'ai tendance à éviter toute utilisation explicite de Observable.create dans la mesure du possible, car en général c'est une source de bugs pour gérer non seulement votre émission d'événement mais aussi votre logique de démontage.

Vous pouvez utiliser Observable.defer à la place. Il accepte une fonction qui renvoie un Observable ou un Observable-like chose (lire: Promise, Array, Iterators). Donc, si vous avez une fonction qui renvoie une chose asynchrone, c'est aussi simple que:

Observable.defer(() => doSomethingAsync());

Si vous souhaitez que cela fonctionne avec un résultat synchrone, procédez comme suit:

Observable.defer(() => Observable.of(doSomethingSync()));

Remarque: comme create, cela réexécutera la fonction sur chaque abonnement. C'est différent alors dites le résultat de Observable.bindCallback qui stocke le résultat de l'appel de fonction sans réexécuter la fonction. Donc, si vous avez besoin de ce type de comportement, vous devrez utiliser l'opérateur multicasting approprié.

20
paulpdaniels

Une implémentation d'un fromFunction$ que j'ai utilisé dans mon projet:

function fromFunction$<T>(factory: () => T): Observable<T> {
    return Observable.create((observer: Subscriber<T>) => {
        try {
            observer.next(factory());
            observer.complete();
        } catch (error) {
            observer.error(error);
        }
    });
}

Utilisé comme:

fromFunction$(() => 0).subscribe((value) => console.log(`Value is '${value}'`), null, () => console.log('Completed'));
fromFunction$(() => [1, 2, 3]).subscribe((value) => console.log(`Value is '${value}'`), null, () => console.log('Completed'));
fromFunction$(() => { throw 'Something' }).subscribe(null, (error) => console.error(`Error: ${error}`));

Donne:

Value is '0'
Completed

Value is '1,2,3'
Completed

Error: Something

Jusqu'à ce qu'une telle mise en œuvre existe.

4
Matt

En fait, je pense que la meilleure option est d'utiliser Observable.create Car c'est la solution la plus universelle pour les valeurs initiales synchrones et asynchrones.

Si vous êtes sûr que vous utiliserez une fonction synchrone, vous pouvez utiliser l'opérateur startWith() (cela ne fait sens que si la valeur de retour de getSomeValue() doit être la même pour tous les observateurs).

L'utilisation de Observable.bindCallback Comme source observable est bien sûr faisable mais je recommande personnellement de l'éviter car cela rend votre code très difficile à comprendre et ce n'est généralement pas nécessaire car vous pouvez utiliser juste Observable.create.

2
martin