Jak zwrócić odpowiedź z wywołania Observable / http / async w angular?

110

Mam usługę, która zwraca obserwowalny, który wysyła żądanie http do mojego serwera i pobiera dane. Chcę użyć tych danych, ale zawsze otrzymuję undefined. Jaki jest problem?

Usługa :

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

Składnik:

@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!
  }
}

Sprawdziłem, jak zwrócić odpowiedź z wywołania asynchronicznego? opublikuj, ale nie mogłem znaleźć rozwiązania

eko
źródło
2
byłby to dobry punkt do podkreślenia faktu, że nie jest możliwe przekształcenie operacji asychronicznej w operację synchroniczną.
n00dl3
@ n00dl3 Dzięki za wskazówkę! Próbowałem wyjaśnić w sekcji „Czego nie powinieneś robić:”. Zapraszam do edycji.
eko,
Czy to odpowiada na twoje pytanie? Jak zwrócić odpowiedź z wywołania asynchronicznego?
Heretic Monkey
@HereticMonkey ten post został już zaksięgowany w odpowiedzi
eko
@eko A? Czy to sprawia, że pytanie jest mniej duplikatem?
Heretic Monkey

Odpowiedzi:

117

Powód:

Powodem jest undefinedto, że wykonujesz operację asynchroniczną. Oznacza to, że ukończenie getEventListmetody zajmie trochę czasu (w zależności głównie od szybkości sieci).

Spójrzmy więc na wywołanie http.

this.es.getEventList()

Po wykonaniu („odpaleniu”) żądania http subscribebędziesz czekać na odpowiedź. Podczas oczekiwania javascript wykona wiersze poniżej tego kodu i jeśli napotka synchroniczne przypisania / operacje, wykona je natychmiast.

Więc po zasubskrybowaniu getEventList()i oczekiwaniu na odpowiedź,

console.log(this.myEvents);

linia zostanie wykonana natychmiast. A wartość tego jest undefinedzanim odpowiedź nadejdzie z serwera (lub do czegokolwiek, co zainicjowałeś w pierwszej kolejności).

Jest to podobne do zrobienia:

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

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


Rozwiązanie:

Jak więc rozwiązać ten problem? Użyjemy funkcji callback, która jest subscribemetodą. Ponieważ kiedy dane dotrą z serwera, znajdą się w środku subscribez odpowiedzią.

Więc zmieniając kod na:

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

wypisze odpowiedź ... po pewnym czasie.


Co powinieneś zrobić:

Z odpowiedzią może być wiele innych rzeczy niż tylko jej rejestrowanie; powinieneś wykonać wszystkie te operacje wewnątrz wywołania zwrotnego (wewnątrz subscribefunkcji), kiedy nadejdą dane.

Inną rzeczą, o której warto wspomnieć, jest to, że jeśli pochodzisz z Promisetła, thenwywołanie zwrotne odpowiada subscribeobserwabli.


Czego nie powinieneś robić:

Nie należy próbować zmieniać operacji asynchronicznej na operację synchronizacji (nie jest to możliwe). Jednym z powodów, dla których mamy operacje asynchroniczne, jest to, że nie zmuszamy użytkownika do czekania na zakończenie operacji, podczas gdy mogą wykonywać inne czynności w tym okresie. Załóżmy, że ukończenie jednej z operacji asynchronicznych zajmuje 3 minuty, a gdybyśmy nie mieli operacji asynchronicznych, interfejs zawiesiłby się na 3 minuty.


Sugerowane czytanie:

Pierwotny zasługa tej odpowiedzi to: Jak zwrócić odpowiedź z wywołania asynchronicznego?

Ale w wydaniu angular2 zostaliśmy wprowadzeni do maszynopisu i obserwabli, więc mam nadzieję, że ta odpowiedź obejmuje podstawy obsługi żądania asynchronicznego z obserwablami.

eko
źródło
3
Kiedy pytający odpowiada na pytanie dokładnie w tym samym czasie, co post .. To jest dobre miejsce, aby zatrzymać się i napisać wpis na blogu
Jonas Praem,
3
@JonasPraem Prawda, ale nie ma nic złego w dzieleniu się wiedzą na temat Stackoverflow. Jak widzisz liczbę głosów, pomogło to wielu osobom tutaj i nadal będzie to robić.
Amit Chigadani
11

Wykonywanie wywołania http w angular / javascript jest operacją asynchroniczną. Więc kiedy wykonasz wywołanie http, przypisze nowy wątek, aby zakończyć to wywołanie i rozpocząć wykonywanie następnej linii z innym wątkiem. Dlatego otrzymujesz nieokreśloną wartość. więc wprowadź poniżej zmiany, aby rozwiązać ten problem

this.es.getEventList()  
      .subscribe((response)=>{  
       this.myEvents = response;  
        console.log(this.myEvents); //<-this become synchronous now  
    });
RAVI PATEL
źródło
5

Możesz użyć asyncPipe, jeśli używasz myEvents tylko w szablonie.

Oto przykład z asyncPipe i Angular4 HttpClient przykład

Kliment Ru
źródło
1
I (!) Dzięki tej implementacji nie trzeba rezygnować z subskrypcji: medium.com/@sub.metu/…
San Jay Falcon
@SanJayFalcon Ponieważ jest to żądanie http, nie ma potrzeby anulowania subskrypcji.
AJT82
3

Observables są leniwe, więc musisz subskrybować, aby uzyskać wartość. Zasubskrybowałeś go poprawnie w swoim kodzie, ale jednocześnie zarejestrowałeś dane wyjściowe poza blokiem „subskrybuj”. Dlatego jest „nieokreślony”.

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

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

Więc jeśli zarejestrujesz go w bloku subskrypcji, zarejestruje odpowiedź poprawnie.

ngOnInit(){
  this.es.getEventList()
    .subscribe((response)=>{
        this.myEvents = response;
        console.log(this.myEvents); //Inside the subscribe block 'http response'
    });
}
Kooldandy
źródło
3

Tutaj problem polega na tym, że inicjalizujesz this.myEventsw subscribe()którym jest blok asynchroniczny, podczas gdy robisz console.log()po prostu poza subscribe()blokiem. Więc console.log()wywołanie przed this.myEventszostanie zainicjalizowane.

Przenieś swój kod console.log () również do środka subscribe () i gotowe.

ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
            console.log(this.myEvents);
        });
  }
Suneet Bansal
źródło
2

Wynik jest niezdefiniowany, ponieważ proces kątowy jest asynchroniczny. możesz spróbować jak poniżej:

async ngOnInit(){
    const res = await this.es.getEventList();
    console.log(JSON.stringify(res));
}
NhutLe
źródło
1

Upewnij się również, że mapujesz swoją odpowiedź na dane wyjściowe JSON. W przeciwnym razie zwróci zwykły tekst. Robisz to tak:

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;})
}
luukgruijs
źródło
1

Niezdefiniowane, ponieważ wartość tutaj jest rejestrowana, zanim jakiekolwiek dane z usługi zostaną ustawione na podstawie powyższego wywołania usługi subskrypcji. Musisz więc poczekać, aż wywołanie Ajax zakończy się i ustawić dane z danych odpowiedzi.

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

Tutaj zrób dziennik konsoli w metodzie subscribe, która będzie logować, gdy dane są ustawione w zmiennej myEvents.

ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
     // This prints the value from the response
    console.log(this.myEvents)
        }); 
  }
Hitech Hitesh
źródło
0

Możesz po prostu wypróbować tę metodę-

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');
    }
Ostre
źródło