Comment renvoyer la réponse d'un appel Observable / http / async en angular?

110

J'ai un service qui renvoie un observable qui fait une requête http à mon serveur et récupère les données. Je veux utiliser ces données mais je finis toujours par obtenir undefined. Quel est le problème?

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

Composant:

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

J'ai vérifié Comment renvoyer la réponse d'un appel asynchrone? poste mais je n'ai pas trouvé de solution

eko
la source
2
ce serait un bon point à souligner sur le fait qu'il n'est pas possible de transformer une opération asychrone en une opération synchrone.
n00dl3
@ n00dl3 Merci pour le tuyau! J'ai essayé d'expliquer dans la section "Ce que vous ne devriez pas faire:". N'hésitez pas à le modifier.
eko le
Est-ce que cela répond à votre question? Comment renvoyer la réponse d'un appel asynchrone?
Heretic Monkey
@HereticMonkey ce message est déjà crédité dans la réponse
eko
@eko Et? Cela rend-il la question moins dupliquée?
Heretic Monkey le

Réponses:

117

Raison:

La raison en undefinedest que vous effectuez une opération asynchrone. Cela signifie qu'il faudra un certain temps pour terminer la getEventListméthode (en fonction principalement de la vitesse de votre réseau).

Regardons donc l'appel http.

this.es.getEventList()

Après avoir réellement fait ("feu") votre requête http avec subscribevous attendra la réponse. En attendant, javascript exécutera les lignes sous ce code et s'il rencontre des affectations / opérations synchrones, il les exécutera immédiatement.

Donc, après s'être abonné au getEventList()et attendre la réponse,

console.log(this.myEvents);

ligne sera exécutée immédiatement. Et la valeur de celui-ci est undefinedavant que la réponse n'arrive du serveur (ou à tout ce que vous avez initialisé en premier lieu).

C'est similaire à faire:

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

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


Solution:

Alors, comment pouvons-nous surmonter ce problème? Nous utiliserons la fonction de rappel qui est la subscribeméthode. Parce que lorsque les données arrivent du serveur, elles seront à l'intérieur du subscribeavec la réponse.

Changez donc le code en:

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

imprimera la réponse .. après un certain temps.


Ce que tu devrais faire:

Il peut y avoir beaucoup de choses à faire avec votre réponse autres que la simple consignation; vous devez faire toutes ces opérations à l'intérieur du rappel (à l'intérieur de la subscribefonction), lorsque les données arrivent.

Une autre chose à mentionner est que si vous venez d'un Promisearrière - plan, le thenrappel correspond à subscribedes observables.


Ce que vous ne devriez pas faire:

Vous ne devriez pas essayer de changer une opération asynchrone en une opération de synchronisation (ce n'est pas possible). L'une des raisons pour lesquelles nous avons des opérations asynchrones est de ne pas obliger l'utilisateur à attendre la fin d'une opération alors qu'il peut faire d'autres choses pendant cette période. Supposons que l'une de vos opérations asynchrones prenne 3 minutes, si nous n'avions pas les opérations asynchrones, l'interface se figerait pendant 3 minutes.


Lecture suggérée:

Le mérite original de cette réponse va à: Comment puis-je renvoyer la réponse d'un appel asynchrone?

Mais avec la version angular2, nous avons été introduits à la dactylographie et aux observables, donc cette réponse couvre, espérons-le, les bases de la gestion d'une requête asynchrone avec des observables.

eko
la source
3
Lorsqu'une question est répondue par le questionneur au même moment exact de la publication .. C'est un bon endroit pour s'arrêter et écrire un article de blog à la place
Jonas Praem
3
@JonasPraem C'est vrai, mais il n'y a rien de mal à partager les connaissances sur Stackoverflow. Comme vous voyez le nombre de votes, cela a aidé beaucoup de gens ici et cela continuera de le faire.
Amit Chigadani
11

Faire un appel http en angular / javascript est une opération asynchrone. Ainsi, lorsque vous effectuez un appel http, il affectera un nouveau thread pour terminer cet appel et démarrer l'exécution de la ligne suivante avec un autre thread. C'est pourquoi vous obtenez une valeur indéfinie. alors faites le changement ci-dessous pour résoudre ce problème

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

Vous pouvez utiliser asyncPipe si vous utilisez myEvents uniquement dans le modèle.

Ici , par exemple avec asyncPipe et Angular4 HttpClient exemple

Kliment Ru
la source
1
Et (!) Avec cette implémentation, il n'est pas nécessaire de se désabonner de l'abonnement: medium.com/@sub.metu
San Jay Falcon
@SanJayFalcon Puisqu'il s'agit d'une requête http, il n'est pas nécessaire de se désabonner.
AJT82
3

Les observables sont paresseux, vous devez donc vous abonner pour obtenir la valeur. Vous l'avez correctement abonné dans votre code, mais vous avez simultanément enregistré la sortie en dehors du bloc «s'abonner». C'est pourquoi il est «indéfini».

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

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

Donc, si vous le connectez dans le bloc d'abonnement, il enregistrera correctement la réponse.

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

Ici, le problème est que vous êtes en train d' initialiser this.myEventsdans subscribe()lequel se trouve un bloc asynchrone alors que vous faites console.log()juste hors du subscribe()bloc. Donc, console.log()être appelé avant this.myEventsest initialisé.

Veuillez également déplacer votre code console.log () dans subscribe () et vous avez terminé.

ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
            console.log(this.myEvents);
        });
  }
Suneet Bansal
la source
2

Le résultat n'est pas défini car le processus angulaire est asynchrone. vous pouvez essayer comme ci-dessous:

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

Assurez-vous également de mapper votre réponse à une sortie json. Sinon, il renverra du texte brut. Vous le faites comme ceci:

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
la source
1

Non défini car la valeur ici est enregistrée avant que les données du service ne soient définies à partir de l'appel de service d'abonnement ci-dessus. Vous devez donc attendre la fin de l'appel ajax et définir les données à partir des données de réponse.

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

Ici, créez le journal de la console dans la méthode d'abonnement qui créera le journal lorsque les données sont définies dans la variable myEvents.

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

Vous pouvez simplement essayer cette méthode-

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');
    }
Harshit
la source