Comment retourner la valeur de la fonction qui a un abonnement Observable à l'intérieur?

97

Je ne sais pas comment extraire la valeur d'Observable pour qu'elle soit renvoyée par la fonction dans laquelle Observable est présent. J'ai juste besoin d'une valeur de celui-ci pour être retourné, rien d'autre.

Version actuelle qui fonctionne

function getValueFromObservable() {
    this.store.subscribe(
        (data:any) => {
            console.log(data)
        }
    )
}
getValueFromObservable()

J'ai besoin de cela pour fonctionner, fonction pour renvoyer la valeur, puis:

function getValueFromObservable() {
    this.store.subscribe(
        (data:any) => {
            return data
        }
    )
}
console.log(getValueFromObservable())

Qu'est-ce que je fais de mal ici?

Teddy
la source
2
Vous devriez renvoyer une Observable / Promise et transmettre les données via celle-ci lorsque votre observable est résolue
galvan
2
Pouvez-vous mettre un code simple pour cela?
Teddy
6
Ce que vous essayez de réaliser est un anti-pattern: vous essayez de "synchroniser" une tâche asynchrone. Ce n'est pas ainsi que les observables sont censés fonctionner. En bref, dans la plupart des cas, une fonction ayant une observable comme entrée doit également renvoyer une observable - ou ne rien renvoyer. Et lorsque vous avez besoin de faire quelque chose avec la sortie, abonnez-vous. Dans ce cas, si vous voulez console.log les données, faites-le simplement à l'intérieursubscribe
Can Nguyen
1
Je comprends tout ce que tu as dit. J'utilise juste le journal de la console comme démo, j'utiliserai ces données plus loin, c'est pourquoi j'en ai besoin pour le journal de la console en dehors de l'observable. Le but est d'avoir la fonction qui, lorsque vous pouvez souscrire l'observable, obtenir des données, se désabonner et renvoyer des données dans cette fonction afin que je puisse utiliser ces données davantage. Je sais que c'est anti-modèle, mais j'en ai besoin pour fonctionner. Toute aide est appréciée. Actuellement, ma solution fonctionne, mais je ne suis pas trop confiant à ce sujet.
Teddy du
4
Attention, s'il vous plaît! Le code de la section «SOLUTION» est absolument incorrect. Ne l'utilisez pas! Cela ne fonctionnera que si la section this.store.subscribe ((data: any) => {output = data}) .unsubscribe () sera terminée jusqu'au retour. Sinon, il retournera undefined.
Rodion Golovushkin

Réponses:

56

EDIT: code mis à jour afin de refléter les modifications apportées au fonctionnement des tuyaux dans les versions plus récentes de RXJS. Tous les opérateurs (prenons mon exemple) sont maintenant enveloppés dans l'opérateur pipe ().

Je me rends compte que cette question remonte à un certain temps et que vous avez sûrement une solution appropriée maintenant, mais pour tous ceux qui recherchent cela, je suggérerais de la résoudre avec une promesse de garder le modèle asynchrone.

Une version plus verbeuse créerait une nouvelle promesse:

function getValueFromObservable() {
    return new Promise(resolve=>{
        this.store.pipe(
           take(1) //useful if you need the data once and don't want to manually cancel the subscription again
         )
         .subscribe(
            (data:any) => {
                console.log(data);
                resolve(data);
         })
    })
}

À la réception, vous devrez alors «attendre» que la promesse se résout avec quelque chose comme ceci:

getValueFromObservable()
   .then((data:any)=>{
   //... continue with anything depending on "data" after the Promise has resolved
})

Une solution plus mince consisterait à utiliser .toPromise () de RxJS à la place:

function getValueFromObservable() {
    return this.store.pipe(take(1))
       .toPromise()   
}

Le côté récepteur reste bien sûr le même que ci-dessus.

jparg
la source
Quel est le type de retour de votre getValueFromObservablefonction?
hardywang
Devrait être une promesse quel que soit le type de données du magasin. Exemple: Promise <StoreType>
jparg
4
vous renvoyez toujours une promesse qui doit être résolue et ne renvoyez pas directement une valeur.
Roj
1
La propriété 'take' n'existe pas sur le type 'Observable <>'
Memmo
1
@Memmo essayez .pipe (prenez (1)) à la place
Thibault
20

Ce n'est pas exactement l'idée correcte d'utiliser Observable

Dans le composant, vous devez déclarer un membre de classe qui contiendra un objet (quelque chose que vous allez utiliser dans votre composant)

export class MyComponent {
  name: string = "";
}

Ensuite, vous Serviceserez renvoyé un Observable:

getValueFromObservable():Observable<string> {
    return this.store.map(res => res.json());
}

Component devrait se préparer pour pouvoir en récupérer une valeur:

OnInit(){
  this.yourServiceName.getValueFromObservable()
    .subscribe(res => this.name = res.name)
}

Vous devez attribuer une valeur de an Observableà une variable:

Et votre modèle consommera la variable name:

<div> {{ name }} </div>

Une autre façon d'utiliser Observableest via le asynctuyau http://briantroncone.com/?p=623

Remarque : si ce n'est pas ce que vous demandez, veuillez mettre à jour votre question avec plus de détails

Andrei Zhytkevich
la source
Enfin pas tout à fait. Le problème est que les données sont capturées à l'intérieur de l'observable et que je peux simplement les consigner par console. Je veux retourner cette valeur et console.log ou quoi que ce soit à partir d'un fichier différent en appelant la fonction dans laquelle il réside.
Teddy
Andrei a expliqué comment rendre le namedisponible en dehors du rappel en l'attribuant à la namevariable du composant . Il n'est pas possible de revenir de manière namesynchrone dans votre cas.
Matt
@Matt: Je ne peux pas l'utiliser Oninitcomme ça, que faire si je dois revenir explicitement, mon code d'appel ressemble à ceci this.actions$.ofType(SearchActions.SEARCH_MULTIPLE_NEW_QUERY).map(toPayload).fnWithMultipleAsyncReturns()
ishandutta2007
@ ishandutta2007 Salut. Vous feriez mieux de créer une nouvelle question sur SO concernant votre problème.
Matt
@Matt: créé, au cas où vous voudriez jeter un oeil ( stackoverflow.com/questions/43381922/… )
ishandutta2007
7

Si vous souhaitez vous préinscrire au même Observable qui vous sera retourné, utilisez simplement

.faire():

function getValueFromObservable() {
    return this.store.do(
        (data:any) => {
            console.log("Line 1: " +data);
        }
    );
}

getValueFromObservable().subscribe(
        (data:any) => {
            console.log("Line 2: " +data)
        }
    );
Dudi
la source
3
Vous pouvez également utiliser d'autres opérateurs comme .map(data => data)qui fait la même chose, puis vous abonner là où vous attendez le résultat
ashok_khuman
Je suis d'accord avec ashok_khuman. Voici le guide angular.io/guide/pipes
Armando Perea
Cela pourrait être une bonne réponse, mais en fait, vous n'avez rien expliqué à ce sujet, cela en fait une mauvaise réponse. Que signifie «pré-abonnement»? Et devrait-il résoudre la question de l'ouvre-fil?
Florian Leitgeb
notez que dans RxJS 6 doest maintenant appelé tapet vous devez l'utiliser dans un tube. Notez également que tapprend plusieurs paramètres pour différents gestionnaires tels que next, completeet error.
Simon_Weaver
7

Le problème est que les données sont capturées à l'intérieur de l'observable et que je peux simplement les consigner par console. Je veux retourner cette valeur et console.log ou quoi que ce soit à partir d'un fichier différent en appelant la fonction dans laquelle il réside.

On dirait que vous cherchez un getter "valeur courante" à l'intérieur d'un observable, quand il émet et après une émission.

Subjectet Observablen'a pas une telle chose. Lorsqu'une valeur est émise, elle est transmise à ses abonnés et en a Observableterminé avec elle.

Vous pouvez utiliser BehaviorSubjectqui stocke la dernière valeur émise et l'émet immédiatement aux nouveaux abonnés.

Il a également une getValue()méthode pour obtenir la valeur actuelle;

Lectures complémentaires:

RxJS BehaviorSubject

Comment obtenir la valeur actuelle de RxJS Subject ou Observable?

A. Alencar
la source
2

Les valeurs observables peuvent être récupérées à partir de n'importe quel emplacement. La séquence source est d'abord poussée sur un observateur spécial capable d'émettre ailleurs. Ceci est réalisé avec la classe Subject des extensions réactives (RxJS).

var subject = new Rx.AsyncSubject();  // store-last-value method

Stockez la valeur sur l' observateur .

subject.next(value); // store value
subject.complete(); // publish only when sequence is completed

Pour récupérer la valeur ailleurs, abonnez-vous à l'observateur comme ceci:

subject.subscribe({
  next: (response) => {
      //do stuff. The property name "response" references the value
  }
});

Les sujets sont à la fois des observables et des observateurs. Il existe d'autres types de sujets tels que BehaviourSubject et ReplaySubject pour d'autres scénarios d'utilisation.

N'oubliez pas d'importer RxJS.

var Rx = require('rxjs');
Studio Pageii
la source
1

Bien que les réponses précédentes puissent fonctionner d'une certaine manière, je pense que l'utilisation de BehaviorSubject est la bonne façon si vous souhaitez continuer à utiliser des observables.

Exemple:

    this.store.subscribe(
        (data:any) => {
            myService.myBehaviorSubject.next(data)
        }
    )

Dans le service:

let myBehaviorSubject = new BehaviorSubjet(value);

Dans component.ts:

this.myService.myBehaviorSubject.subscribe(data => this.myData = data)

J'espère que ça aide!

Shlomo Koppel
la source
0

Par exemple, voici mon modèle html:

<select class="custom-select d-block w-100" id="genre" name="genre"
                  [(ngModel)]="film.genre"
                  #genreInput="ngModel"
                  required>
            <option value="">Choose...</option>
            <option *ngFor="let genre of genres;" [value]="genre.value">{{genre.name}}</option>
          </select>

Voici le champ lié au modèle de mon composant:

  // Genres of films like action or drama that will populate dropdown list.
  genres: Genre[];

Je récupère les genres de films du serveur de manière dynamique. Afin de communiquer avec le serveur que j'ai crééFilmService

C'est la méthode qui communique le serveur:

 fetchGenres(): Observable<Genre[]> {
    return this.client.get(WebUtils.RESOURCE_HOST_API + 'film' + '/genre') as Observable<Genre[]>;
  }

Pourquoi cette méthode Observable<Genre[]>ne renvoie pas quelque chose comme Genre[]?

JavaScript est asyncet n'attend pas qu'une méthode retourne de la valeur après un processus coûteux. Avec cher, je veux dire un processus qui prend du temps à retourner de la valeur. Comme récupérer des données sur le serveur. Vous devez donc renvoyer la référence d'Observable et vous y abonner.

Par exemple dans mon composant:

ngOnInit() {
    this.filmService.fetchGenres().subscribe(
      val => this.genres = val
    );
  }
Muhammed Ozdogan
la source
0
function getValueFromObservable() {
    this.store.subscribe(
        (data:any) => {
            return data
        }
    )
}
console.log(getValueFromObservable())

Dans le cas ci-dessus, console.log s'exécute avant que la promesse ne soit résolue, donc aucune valeur ne s'affiche, changez-le en suivant

function getValueFromObservable() {
    return this.store
}

getValueFromObservable()
 .subscribe((data: any) => {
    // do something here with data
    console.log(data);
});

Une autre solution est lorsque vous avez besoin de données dans getValueFromObservable pour renvoyer l'observable à l'aide de l'opérateur of et vous abonner à la fonction.

 function getValueFromObservable() {
        return this.store.subscribe((data: any) => {
            // do something with data here
            console.log(data);
            //return again observable.
            return of(data);
       })
    }

    getValueFromObservable()
     .subscribe((data: any) => {
        // do something here with data
        console.log(data);
    });
iaq
la source
0

Dans le monde de javascript à un seul thread, asynchrone, orienté promesse et réactif, se async/awaittrouve le meilleur ami du programmeur de style impératif:

(async()=>{

    const store = of("someValue");
    function getValueFromObservable () {
        return store.toPromise();
    }
    console.log(await getValueFromObservable())

})();

Et au cas où il storey aurait une séquence de plusieurs valeurs:

  const aiFrom = require('ix/asynciterable').from;
  (async function() {

     const store = from(["someValue","someOtherValue"]);
     function getValuesFromObservable () {
        return aiFrom(store);
     }
     for await (let num of getValuesFromObservable()) {
       console.log(num);
     }
  })();
Marinos An
la source
0

La manière décente serait de renvoyer l'observable à partir d'une fonction et de s'y abonner partout où cela est nécessaire, car les observables sont paresseux, ils ne commenceront à émettre des valeurs que lorsqu'ils seront abonnés.

Ici, j'ai une autre solution événementielle intéressante, avec laquelle j'ai d'abord joué. L'exemple suivant fait cela en utilisant le module " events " de nodejs. Vous pouvez l'utiliser avec d'autres frameworks où un module similaire existe ( Remarque : la syntaxe et le style peuvent changer en fonction du module utilisé).

var from =require("rxjs").from;
var map = require("rxjs/operators").map;
var EventEmitter = require("events");

function process(event) {
    from([1,2,3]).pipe(
        map(val => `The number is:: ${val}`)
    ).subscribe((data) => {
       event.emit("Event1", data); //emit value received in subscribe to the "Event1" listener
    });
}

function main() {
   class Emitter extends EventEmitter{};
    var event = new Emitter(); //creating an event
    event.on("Event1", (data)=>{ //listening to the event of name "Event1" and callback to log returned result
        console.log(data); //here log, print, play with the data you receive
    });
    process(event); //pass the event to the function which returns observable.
}

main(); //invoke main function

C'est juste un exemple pour présenter une idée où nous pouvons transmettre des données de différents endroits par une méthode d'émission et d'écoute. Ceci est également connu sous le nom de code événementiel.

varad11
la source