Como eu retorno a resposta de uma chamada Observable / http / async no angular?

110

Eu tenho um serviço que retorna um observável que faz uma solicitação http ao meu servidor e obtém os dados. Quero usar esses dados mas sempre acabo conseguindo undefined. Qual é o problema?

Serviço :

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

Eu verifiquei Como faço para retornar a resposta de uma chamada assíncrona? postar, mas não consegui encontrar uma solução

eko
fonte
2
isso seria um bom ponto a enfatizar sobre o fato de não ser possível transformar uma operação assíncrona em síncrona.
n00dl3
@ n00dl3 Obrigado pela dica! Tentei explicar na seção "O que você não deve fazer:". Sinta-se à vontade para editá-lo.
eko de
Isso responde sua pergunta? Como faço para retornar a resposta de uma chamada assíncrona?
Macaco herege
@HereticMonkey esse post já foi creditado na resposta
eko 24/04
@eko E? Isso torna a pergunta menos duplicada?
Macaco herege

Respostas:

117

Razão:

A razão disso undefinedé que você está fazendo uma operação assíncrona. Isso significa que levará algum tempo para concluir o getEventListmétodo (dependendo principalmente da velocidade da rede).

Então, vamos dar uma olhada na chamada http.

this.es.getEventList()

Depois de realmente fazer ("disparar") sua solicitação http, subscribevocê estará aguardando a resposta. Enquanto espera, o javascript executará as linhas abaixo deste código e se encontrar atribuições / operações síncronas, ele as executará imediatamente.

Então, depois de se inscrever no getEventList()e esperar pela resposta,

console.log(this.myEvents);

linha será executada imediatamente. E o valor disso é undefinedantes que a resposta chegue do servidor (ou de qualquer coisa que você inicializou em primeiro lugar).

É semelhante a fazer:

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

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


Solução:

Então, como podemos superar esse problema? Usaremos a função de retorno de chamada que é o subscribemétodo. Porque quando os dados chegarem do servidor estarão dentro do subscribecom a resposta.

Portanto, alterando o código para:

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

irá imprimir a resposta .. depois de algum tempo.


O que você deveria fazer:

Pode haver muitas coisas a fazer com sua resposta além de apenas registrá-la; você deve fazer todas essas operações dentro do callback (dentro da subscribefunção), quando os dados chegarem.

Outra coisa a mencionar é que se você vier de um Promiseplano de fundo, o thenretorno de chamada corresponderá a subscribeobserváveis.


O que você não deve fazer:

Você não deve tentar alterar uma operação assíncrona para uma operação de sincronização (não que você possa). Um dos motivos pelos quais temos operações assíncronas é não fazer o usuário esperar a conclusão de uma operação enquanto ele pode fazer outras coisas nesse período de tempo. Suponha que uma de suas operações assíncronas leve 3 minutos para ser concluída. Se não tivéssemos as operações assíncronas, a interface congelaria por 3 minutos.


Leitura sugerida:

O crédito original para esta resposta vai para: Como retorno a resposta de uma chamada assíncrona?

Mas com o lançamento do angular2, fomos apresentados ao texto datilografado e aos observáveis, portanto, esta resposta esperançosamente cobre os fundamentos do tratamento de uma solicitação assíncrona com os observáveis.

eko
fonte
3
Quando uma pergunta é respondida pelo questionador exatamente no mesmo momento da postagem .. Este é um bom lugar para parar e escrever uma postagem no blog
Jonas Praem
3
@JonasPraem True, mas não há nada de errado em compartilhar o conhecimento no Stackoverflow. Como você vê o número de votos, ajudou muitas pessoas aqui e continuará ajudando.
Amit Chigadani
11

Fazer uma chamada http em angular / javascript é uma operação assíncrona. Portanto, quando você fizer uma chamada http, ele atribuirá um novo encadeamento para finalizar esta chamada e iniciar a execução da próxima linha com outro encadeamento. É por isso que você está obtendo valor indefinido. então faça as alterações abaixo para resolver isso

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

Você pode usar asyncPipe se usar myEvents apenas no modelo.

Aqui, exemplo com asyncPipe e Angular4 HttpClient exemplo

Kliment Ru
fonte
1
E (!) Com esta implementação, não é necessário cancelar a assinatura: medium.com/@sub.metu/…
San Jay Falcon
@SanJayFalcon Como esta é uma solicitação http, não há necessidade de cancelar a assinatura.
AJT82 de
3

Os observáveis ​​são preguiçosos, então você precisa se inscrever para obter o valor. Você o inscreveu corretamente em seu código, mas simultaneamente registrou a saída fora do bloco de 'inscrição'. É por isso que é 'indefinido'.

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

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

Portanto, se você registrá-lo dentro do bloco de inscrição, ele registrará a resposta corretamente.

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

Aqui o problema é que você está inicializando this.myEventsem subscribe()um bloco assíncrono enquanto está fazendo console.log()apenas fora do subscribe()bloco. Portanto, console.log()ser chamado antes de this.myEventsser inicializado.

Mova seu código console.log () também dentro de subscribe () e pronto.

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

O resultado é indefinido porque o processo angular é assíncrono. você pode tentar o seguinte:

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

Certifique-se também de mapear sua resposta para uma saída json. Caso contrário, ele retornará texto simples. Você faz assim:

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

Indefinido porque o valor aqui é registrado antes de qualquer dado do serviço ser definido a partir da chamada de serviço de assinatura acima. Portanto, você deve esperar até que a chamada ajax termine e definir os dados dos dados de resposta.

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

Aqui, faça o log do console dentro do método de inscrição que fará o log quando os dados forem configurados na variável myEvents.

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

Você pode simplesmente tentar este método-

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');
    }
Merda
fonte