Wie kann ich die Antwort von einem Observable / http / async-Aufruf im Winkel zurückgeben?


110

Ich habe einen Dienst, der eine Observable zurückgibt, die eine http-Anfrage an meinen Server sendet und die Daten abruft. Ich möchte diese Daten verwenden, bekomme sie aber immer wieder undefined. Was ist das Problem?

Service :

@Injectable()
export class EventService {

  constructor(private http: Http) { }

  getEventList(): Observable<any>{
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    return this.http.get("http://localhost:9999/events/get", options)
                .map((res)=> res.json())
                .catch((err)=> err)
  }
}

Komponente:

@Component({...})
export class EventComponent {

  myEvents: any;

  constructor( private es: EventService ) { }

  ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
        });

    console.log(this.myEvents); //This prints undefined!
  }
}

Ich habe überprüft, wie ich die Antwort von einem asynchronen Anruf zurückgeben kann. Post konnte aber keine Lösung finden


2
Dies wäre ein guter Punkt, um die Tatsache hervorzuheben, dass es nicht möglich ist, eine asychrone Operation in eine synchrone Operation umzuwandeln.
n00dl3

@ n00dl3 Danke für den Tipp! Ich habe versucht, im Abschnitt "Was Sie nicht tun sollten:" zu erklären. Fühlen Sie sich frei, es zu bearbeiten.
eko


@HereticMonkey dieser Beitrag ist bereits in der Antwort gutgeschrieben
eko

@eko Und? Macht das die Frage weniger zu einem Duplikat?
Heretic Monkey

Antworten:


117

Grund:

Der Grund dafür undefinedist, dass Sie eine asynchrone Operation ausführen. Das bedeutet, dass es einige Zeit dauern wird, bis die getEventListMethode abgeschlossen ist (hauptsächlich abhängig von Ihrer Netzwerkgeschwindigkeit).

Schauen wir uns also den http-Aufruf an.

this.es.getEventList()

Nachdem Sie Ihre http-Anfrage mit Ihnen tatsächlich gestellt ("ausgelöst") subscribehaben, warten Sie auf die Antwort. Während des Wartens führt Javascript die Zeilen unter diesem Code aus und führt sie sofort aus, wenn es auf synchrone Zuweisungen / Operationen stößt.

Nachdem Sie die abonniert getEventList()und auf die Antwort gewartet haben,

console.log(this.myEvents);

Zeile wird sofort ausgeführt. Und der Wert davon ist, undefinedbevor die Antwort vom Server eintrifft (oder zu dem, was Sie zuerst initialisiert haben).

Es ist ähnlich wie:

ngOnInit(){
    setTimeout(()=>{
        this.myEvents = response;
    }, 5000);

    console.log(this.myEvents); //This prints undefined!
}


Lösung:

Wie überwinden wir dieses Problem? Wir werden die Rückruffunktion verwenden, die die subscribeMethode ist. Denn wenn die Daten vom Server ankommen, befinden sie sich innerhalb der subscribemit der Antwort.

Ändern Sie den Code also in:

this.es.getEventList()
    .subscribe((response)=>{
        this.myEvents = response;
        console.log(this.myEvents); //<-- not undefined anymore
    });

druckt die Antwort .. nach einiger Zeit.


Was du machen solltest:

Es kann viele andere Dinge mit Ihrer Antwort zu tun haben, als sie nur zu protokollieren. Sie sollten alle diese Vorgänge innerhalb des Rückrufs (innerhalb der subscribeFunktion) ausführen , wenn die Daten eintreffen.

Eine andere Sache zu erwähnen ist, dass, wenn Sie aus einem PromiseHintergrund kommen, der thenRückruf subscribemit Observablen entspricht.


Was Sie nicht tun sollten:

Sie sollten nicht versuchen, einen asynchronen Vorgang in einen Synchronisierungsvorgang umzuwandeln (nicht, dass Sie dies können). Einer der Gründe für asynchrone Vorgänge besteht darin, dass der Benutzer nicht auf den Abschluss eines Vorgangs warten muss, während er in diesem Zeitraum andere Aufgaben ausführen kann. Angenommen, einer Ihrer asynchronen Vorgänge dauert 3 Minuten. Wenn wir die asynchronen Vorgänge nicht hätten, würde die Schnittstelle 3 Minuten lang einfrieren.


Vorgeschlagene Literatur:

Die ursprüngliche Gutschrift für diese Antwort lautet: Wie kann ich die Antwort von einem asynchronen Anruf zurückgeben?

Mit der Version angle2 wurden wir jedoch mit Typoskript und Observablen vertraut gemacht, sodass diese Antwort hoffentlich die Grundlagen der Behandlung einer asynchronen Anforderung mit Observablen abdeckt.


3
Wenn eine Frage vom Fragesteller genau zur gleichen Zeit des Beitrags beantwortet wird. Dies ist ein guter Ort, um anzuhalten und stattdessen einen Blog-Beitrag zu schreiben
Jonas Praem

3
@ JonasPraem Stimmt, aber es ist nichts Falsches daran, das Wissen über Stackoverflow zu teilen. Wie Sie sehen, hat es vielen Menschen hier geholfen und wird es auch weiterhin tun.
Amit Chigadani

11

Ein http-Aufruf in Angular / Javascript ist eine asynchrone Operation. Wenn Sie also einen http-Aufruf durchführen, wird ein neuer Thread zugewiesen, um diesen Aufruf zu beenden und die Ausführung in der nächsten Zeile mit einem anderen Thread zu starten. Deshalb erhalten Sie einen undefinierten Wert. Nehmen Sie daher die folgenden Änderungen vor, um dies zu beheben

this.es.getEventList()  
      .subscribe((response)=>{  
       this.myEvents = response;  
        console.log(this.myEvents); //<-this become synchronous now  
    });


3

Observables sind faul, daher müssen Sie sich anmelden, um den Wert zu erhalten. Sie haben es ordnungsgemäß in Ihrem Code abonniert, aber gleichzeitig die Ausgabe außerhalb des Blocks "Abonnieren" protokolliert. Deshalb ist es "undefiniert".

ngOnInit() {
  this.es.getEventList()
    .subscribe((response) => {
        this.myEvents = response;
    });

  console.log(this.myEvents); //Outside the subscribe block 'Undefined'
}

Wenn Sie es also im Abonnementblock protokollieren, wird die Antwort ordnungsgemäß protokolliert.

ngOnInit(){
  this.es.getEventList()
    .subscribe((response)=>{
        this.myEvents = response;
        console.log(this.myEvents); //Inside the subscribe block 'http response'
    });
}

3

Hier ist das Problem, Sie initialisieren this.myEventsin subscribe()dem ein asynchroner Block , während Sie tun , console.log()gerade aus dem subscribe()Block. So console.log()genannt , bevor man this.myEventswird initialisiert.

Bitte verschieben Sie Ihren console.log () - Code ebenfalls in subscribe () und Sie sind fertig.

ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
            console.log(this.myEvents);
        });
  }

2

Das Ergebnis ist undefiniert, da der Winkelprozess asynchron ist. Sie können wie folgt versuchen:

async ngOnInit(){
    const res = await this.es.getEventList();
    console.log(JSON.stringify(res));
}

1

Stellen Sie außerdem sicher, dass Sie Ihre Antwort einer JSON-Ausgabe zuordnen. Andernfalls wird Klartext zurückgegeben. Du machst es so:

getEventList(): Observable<any> {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });

return this.http.get("http://localhost:9999/events/get", options)
            .map((res)=>{ return res.json();}) <!-- add call to json here
            .catch((err)=>{return err;})
}

1

Nicht definiert, da der Wert hier protokolliert wird, bevor Daten vom Dienst aus dem oben genannten Abonnementdienstaufruf festgelegt werden. Sie müssen also warten, bis der Ajax-Aufruf beendet ist, und die Daten aus den Antwortdaten festlegen.

getEventList(): Observable<any>{
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    return this.http.get("http://localhost:9999/events/get", options)
                .map((res)=> res.json())
                .catch((err)=> err)
  }

Erstellen Sie hier das Konsolenprotokoll innerhalb der Subscribe-Methode, mit der das Protokoll erstellt wird, wenn die Daten in der Variablen myEvents festgelegt werden.

ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
     // This prints the value from the response
    console.log(this.myEvents)
        }); 
  }

0

Sie können diese Methode einfach ausprobieren.

let headers = new Headers({'Accept': 'application/json'});
let options = new RequestOptions({headers: headers});

return this.http
    .get(this.yourSearchUrlHere, options) // the URL which you have defined
    .map((res) => {
        res.json(); // using return res.json() will throw error
    }
    .catch(err) => {
        console.error('error');
    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.