Wie kann ein Wert von einer Funktion zurückgegeben werden, in der sich ein Observable-Abonnement befindet?

95

Ich weiß nicht, wie ich einen Wert aus Observable extrahieren soll, der von einer Funktion zurückgegeben werden soll, in der Observable vorhanden ist. Ich brauche nur einen Wert davon, um zurückgegeben zu werden, sonst nichts.

Aktuelle Version, die funktioniert

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

Ich brauche das, um zu funktionieren, um den Wert zurückzugeben, und dann:

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

Was mache ich hier falsch?

Teddy
quelle
2
Sie sollten ein Observable / Promise zurückgeben und die Daten darüber
weiterleiten,
2
Können Sie einen einfachen Code dafür eingeben?
Teddy
6
Was Sie erreichen möchten, ist ein Anti-Pattern: Sie versuchen, eine asynchrone Aufgabe zu "synchronisieren". So sollen Observablen nicht funktionieren. Kurz gesagt, in den meisten Fällen sollte eine Funktion mit einem Observable als Eingabe auch ein Observable zurückgeben - oder nichts zurückgeben. Und wenn Sie etwas mit der Ausgabe tun müssen, abonnieren Sie sie. In diesem Fall, wenn Sie console.log die Daten wollen, tun Sie es einfach insubscribe
Can Nguyen
1
Ich verstehe alles, was du gesagt hast. Ich verwende nur das Konsolenprotokoll als Demo. Ich werde diese Daten weiter verwenden. Deshalb benötige ich sie, um das Protokoll außerhalb des Observable zu konsolidieren. Der Punkt ist, die Funktion zu haben, die, wenn Sie das Observable abonnieren, Daten abrufen, abbestellen und Daten in dieser Funktion zurückgeben können, damit ich diese Daten weiter verwenden kann. Ich weiß, dass es ein Anti-Muster ist, aber ich brauche es, um zu funktionieren. Jede Hilfe wird geschätzt. Derzeit funktioniert meine Lösung, aber ich bin nicht sehr zuversichtlich.
Teddy
4
Aufmerksamkeit bitte! Code aus Abschnitt 'LÖSUNG' ist absolut falsch. Benutze es nicht! Dies funktioniert nur, wenn der Abschnitt this.store.subscribe ((data: any) => {output = data}) .unsubscribe () bis zur Rückgabe abgeschlossen ist. Andernfalls wird undefiniert zurückgegeben.
Rodion Golovushkin

Antworten:

54

BEARBEITEN: Der Code wurde aktualisiert, um Änderungen an der Funktionsweise von Pipes in neueren Versionen von RXJS widerzuspiegeln. Alle Operatoren (in meinem Beispiel) sind jetzt in den Operator pipe () eingeschlossen.

Mir ist klar, dass diese Frage schon eine Weile her ist und Sie sicherlich schon eine richtige Lösung gefunden haben, aber für alle, die danach suchen, würde ich vorschlagen, sie mit dem Versprechen zu lösen, das asynchrone Muster beizubehalten.

Eine ausführlichere Version würde ein neues Versprechen erstellen:

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);
         })
    })
}

Auf der Empfangsseite müssen Sie dann "warten", bis das Versprechen mit so etwas gelöst ist:

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

Eine schlankere Lösung wäre stattdessen die Verwendung von .toPromise () von RxJS:

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

Die Empfangsseite bleibt natürlich die gleiche wie oben.

jparg
quelle
Was ist der Rückgabetyp Ihrer getValueFromObservableFunktion?
Hardywang
Sollte ein Versprechen für jeden Typ der Geschäftsdaten sein. zB Versprechen <StoreType>
jparg
4
Sie geben immer noch ein Versprechen zurück, das gelöst werden muss, und geben keinen Wert direkt zurück.
Roj
1
Die Eigenschaft 'take' existiert nicht für den Typ 'Observable <>'
Memmo
1
@Memmo versuche stattdessen .pipe (nimm (1))
Thibault
20

Dies ist nicht genau die richtige Idee der Verwendung Observable

In der Komponente müssen Sie ein Klassenmitglied deklarieren, das ein Objekt enthält (etwas, das Sie in Ihrer Komponente verwenden werden).

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

Dann wird a ServiceIhnen Folgendes zurückgeben Observable:

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

Component sollte sich darauf vorbereiten, einen Wert daraus abrufen zu können:

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

Sie müssen Observableeiner Variablen einen Wert von a zuweisen :

Und Ihre Vorlage wird Variable verbrauchen name:

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

Eine andere Möglichkeit der Verwendung Observableist die asyncPipe http://briantroncone.com/?p=623

Hinweis : Wenn es nicht das ist, was Sie fragen, aktualisieren Sie Ihre Frage bitte mit weiteren Details

Andrei Zhytkevich
quelle
Nicht ganz. Das Problem ist, dass Daten im Observable erfasst werden und ich sie einfach auf der Konsole protokollieren kann. Ich möchte diesen Wert und console.log oder was auch immer aus einer anderen Datei zurückgeben, indem ich die Funktion aufrufe, in der es sich befindet.
Teddy
Andrei wies darauf hin, wie das nameaußerhalb des Rückrufs verfügbar gemacht werden kann, indem es der nameVariablen der Komponente zugewiesen wird. nameIn Ihrem Fall ist es nicht möglich, synchron zurückzukehren.
Matt
@ Matt: Ich kann es nicht so verwenden. OninitWas ist, wenn ich explizit zurückkehren muss this.actions$.ofType(SearchActions.SEARCH_MULTIPLE_NEW_QUERY).map(toPayload).fnWithMultipleAsyncReturns()
?
@ ishandutta2007 Hey da. Sie sollten eine neue Frage zu Ihrem Problem in Bezug auf SO erstellen.
Matt
@Matt: erstellt, falls Sie einen Blick darauf werfen möchten ( stackoverflow.com/questions/43381922/… )
ishandutta2007
7

Wenn Sie dasselbe Observable vorab abonnieren möchten, das zurückgegeben wird, verwenden Sie einfach

.machen():

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

getValueFromObservable().subscribe(
        (data:any) => {
            console.log("Line 2: " +data)
        }
    );
Dudi
quelle
3
Sie können auch andere Operatoren verwenden, .map(data => data)die das Gleiche tun, und sie dann abonnieren, wo immer Sie das Ergebnis erwarten
ashok_khuman
Ich stimme ashok_khuman zu. Hier ist der Leitfaden angle.io/guide/pipes
Armando Perea
Dies könnte eine gute Antwort sein, aber in der Tat, dass Sie nichts darüber erklärt haben, macht es zu einer schlechten Antwort. Was bedeutet "vorab abonnieren"? Und sollte es die Frage vom Threadöffner lösen?
Florian Leitgeb
Beachten Sie, dass in RxJS 6 dojetzt aufgerufen wird tapund Sie es in einer Pipe verwenden müssen. Beachten Sie auch , dass tapmehrere Parameter für verschiedene Handler wie nimmt next, completeund error.
Simon_Weaver
6

Das Problem ist, dass Daten im Observable erfasst werden und ich sie einfach auf der Konsole protokollieren kann. Ich möchte diesen Wert und console.log oder was auch immer aus einer anderen Datei zurückgeben, indem ich die Funktion aufrufe, in der es sich befindet.

Sieht so aus, als würden Sie nach einem "aktuellen Wert" in einem Observablen suchen, wenn er emittiert und nach einer Emission.

Subjectund Observablehat so etwas nicht. Wenn ein Wert ausgegeben wird, wird er an seine Abonnenten übergeben und das Observableist damit erledigt.

Sie können verwenden, BehaviorSubjectwelche den zuletzt ausgegebenen Wert speichert und ihn sofort an neue Abonnenten ausgibt.

Es gibt auch eine getValue()Methode, um den aktuellen Wert abzurufen.

Weiterführende Literatur:

RxJS BehaviorSubject

Wie erhalte ich den aktuellen Wert von RxJS Subject oder Observable?

A. Alencar
quelle
1

Beobachtbare Werte können von jedem Ort abgerufen werden. Die Source - Sequenz wird zuerst geschoben auf einen speziellen Beobachter , die an anderer Stelle zu emittieren vermögen. Dies wird mit der Subject-Klasse aus den Reactive Extensions (RxJS) erreicht.

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

Speichern Sie den Wert auf dem Beobachter .

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

Um den Wert von einer anderen Stelle abzurufen, abonnieren Sie den Beobachter wie folgt:

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

Die Probanden sind sowohl Observable als auch Observer. Es gibt noch andere Fach Typen wie BehaviourSubject und ReplaySubject für andere Nutzungsszenarien.

Vergessen Sie nicht, RxJS zu importieren.

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

Während die vorherigen Antworten möglicherweise auf eine Art und Weise funktionieren, denke ich, dass die Verwendung von BehaviorSubject der richtige Weg ist, wenn Sie weiterhin Observables verwenden möchten.

Beispiel:

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

Im Dienste:

let myBehaviorSubject = new BehaviorSubjet(value);

In component.ts:

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

Ich hoffe das hilft!

Shlomo Koppel
quelle
0

Zum Beispiel ist dies meine HTML-Vorlage:

<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>

Dies ist das Feld, das mit der Vorlage aus meiner Komponente verknüpft wurde:

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

Ich rufe Genres von Filmen dynamisch vom Server ab. Um mit dem von mir erstellten Server zu kommunizierenFilmService

Dies ist die Methode, mit der der Server kommuniziert wird:

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

Warum gibt diese Methode Observable<Genre[]>nicht so etwas zurück Genre[]?

JavaScript ist asyncund wartet nicht darauf, dass eine Methode nach einem teuren Prozess einen Wert zurückgibt. Mit teuer meine ich einen Prozess, dessen Rückgabe einige Zeit in Anspruch nimmt. Wie das Abrufen von Daten vom Server. Sie müssen also die Referenz von Observable zurückgeben und abonnieren.

Zum Beispiel in meiner Komponente:

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

Im obigen Fall wird console.log ausgeführt, bevor das Versprechen aufgelöst wird, sodass kein Wert angezeigt wird. Ändern Sie ihn in den folgenden

function getValueFromObservable() {
    return this.store
}

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

Eine andere Lösung besteht darin, dass Sie Daten in getValueFromObservable benötigen, um den beobachtbaren Operator using zurückzugeben und die Funktion zu abonnieren.

 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
quelle
0

In der asynchronen, vielversprechungsorientierten, reaktiv-trendigen Welt von Javascript mit einem Thread async/awaitist der beste Freund des Programmierers im imperativen Stil:

(async()=>{

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

})();

Und für den Fall storeist eine Folge von mehreren Werten:

  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
quelle
0

Der vernünftige Weg wäre, das Observable von einer Funktion zurückzugeben und es zu abonnieren, wo immer dies erforderlich ist, da Observables faul sind und erst dann Werte ausgeben, wenn sie abonniert sind.

Hier habe ich eine weitere interessante ereignisgesteuerte Lösung, mit der ich ursprünglich herumgespielt habe. Das folgende Beispiel verwendet dazu das Modul " events " von nodejs. Sie können es mit anderen Frameworks verwenden, in denen ein ähnliches Modul vorhanden ist ( Hinweis : Syntax und Stil können sich je nach verwendetem Modul ändern).

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

Es ist nur ein Beispiel, um eine Idee zu präsentieren, bei der wir Daten von verschiedenen Orten durch Senden und Abhören übertragen können. Dies wird auch als ereignisgesteuerter Code bezeichnet.

varad11
quelle