Come restituisco la risposta da una chiamata Observable / http / async in angolare?


110

Ho un servizio che restituisce un osservabile che fa una richiesta http al mio server e ottiene i dati. Voglio usare questi dati ma finisco sempre per ottenere undefined. Qual è il problema?

Servizio :

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

Componente:

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

Ho controllato Come restituisco la risposta da una chiamata asincrona? post ma non sono riuscito a trovare una soluzione


2
sarebbe un buon punto da sottolineare sul fatto che non è possibile trasformare un'operazione asincrona in una sincrona.
n00dl3

@ n00dl3 Grazie per il suggerimento! Ho provato a spiegare nella sezione "Cosa non dovresti fare:". Sentiti libero di modificarlo.
eko


@HereticMonkey quel post è già accreditato nella risposta
eko il

@eko And? Ciò rende la domanda meno duplicata?
Scimmia eretica il

Risposte:


117

Motivo:

Il motivo undefinedè che stai effettuando un'operazione asincrona. Significa che ci vorrà del tempo per completare il getEventListmetodo (dipende principalmente dalla velocità della tua rete).

Quindi diamo un'occhiata alla chiamata http.

this.es.getEventList()

Dopo aver effettivamente fatto ("fuoco") la tua richiesta http con subscribete, sarai in attesa della risposta. Durante l'attesa, javascript eseguirà le righe sotto questo codice e se incontra assegnazioni / operazioni sincrone le eseguirà immediatamente.

Quindi, dopo esserti iscritto a getEventList()e in attesa della risposta,

console.log(this.myEvents);

la linea verrà eseguita immediatamente. E il valore è undefinedprima che la risposta arrivi dal server (o da qualsiasi cosa tu abbia inizializzato in primo luogo).

È simile a fare:

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

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


Soluzione:

Allora come si supera questo problema? Useremo la funzione di callback che è il subscribemetodo. Perché quando i dati arrivano dal server saranno all'interno del subscribecon la risposta.

Quindi, cambiando il codice in:

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

stamperà la risposta .. dopo un po 'di tempo.


Cosa dovresti fare:

Potrebbero esserci molte cose da fare con la tua risposta oltre alla semplice registrazione; dovreste fare tutte queste operazioni all'interno del callback (all'interno della subscribefunzione), quando arrivano i dati.

Un'altra cosa da menzionare è che se vieni da uno Promisesfondo, la thenrichiamata corrisponde a subscribeosservabili.


Cosa non dovresti fare:

Non dovresti provare a cambiare un'operazione asincrona in un'operazione di sincronizzazione (non che tu possa farlo). Uno dei motivi per cui abbiamo operazioni asincrone è di non far attendere il completamento di un'operazione mentre può fare altre cose in quel periodo di tempo. Supponiamo che una delle tue operazioni asincrone richieda 3 minuti per essere completata, se non avessimo le operazioni asincrone l'interfaccia si sarebbe bloccata per 3 minuti.


Lettura suggerita:

Il merito originale di questa risposta va a: come restituisco la risposta da una chiamata asincrona?

Ma con la versione angular2 siamo stati introdotti al dattiloscritto e agli osservabili, quindi si spera che questa risposta copra le basi della gestione di una richiesta asincrona con osservabili.


3
Quando una domanda riceve una risposta dall'interrogante nello stesso momento esatto del post .. Questo è un buon posto per fermarsi e scrivere invece un post sul blog
Jonas Praem

3
@ JonasPraem True, ma non c'è niente di sbagliato nel condividere la conoscenza su Stackoverflow. Come vedi il numero di voti, ha aiutato molte persone qui e continuerà a farlo.
Amit Chigadani

11

Effettuare una chiamata http in angular / javascript è un'operazione asincrona. Quindi, quando effettui una chiamata http, assegnerà un nuovo thread per terminare questa chiamata e avviare l'esecuzione della riga successiva con un altro thread. Questo è il motivo per cui ottieni un valore indefinito. quindi apporta le seguenti modifiche per risolvere il problema

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


3

Gli osservabili sono pigri, quindi devi iscriverti per ottenere il valore. L'hai sottoscritto correttamente nel tuo codice ma contemporaneamente hai registrato l'output al di fuori del blocco "iscriviti". Ecco perché è "indefinito".

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

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

Quindi, se lo accedi all'interno del blocco di iscrizione, registrerà correttamente la risposta.

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

3

Qui il problema è che stai inizializzando this.myEventsin subscribe()cui è un blocco asincrono mentre stai eseguendo console.log()appena fuori subscribe()blocco. Quindi console.log()essere chiamato prima this.myEventsviene inizializzato.

Si prega di spostare anche il codice console.log () all'interno di subscribe () e il gioco è fatto.

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

2

Il risultato non è definito perché il processo angolare è asincrono. puoi provare come di seguito:

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

1

Assicurati inoltre di mappare la tua risposta a un output json. Altrimenti restituirà testo normale. Lo fai in questo modo:

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

Non definito perché il valore qui viene registrato prima che i dati del servizio vengano impostati da quella chiamata di servizio di sottoscrizione sopra. Quindi devi aspettare fino al termine della chiamata ajax e impostare i dati dai dati di risposta.

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

Qui crea il log della console all'interno del metodo di sottoscrizione che farà il log quando i dati sono impostati nella variabile myEvents.

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

0

Puoi semplicemente provare questo metodo-

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');
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.