Observable / http / async 호출의 응답을 각도로 어떻게 반환합니까?


110

내 서버에 http 요청을 수행하고 데이터를 가져 오는 Observable을 반환하는 서비스가 있습니다. 이 데이터를 사용하고 싶지만 항상 undefined. 뭐가 문제 야?

서비스 :

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

구성 요소:

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

내가 확인 하는 방법은 비동기 호출의 응답을 반환합니까? 게시했지만 해결책을 찾지 못했습니다.


2
비동기 작업을 동기 작업으로 변환 할 수 없다는 사실을 강조하는 것이 좋습니다.
n00dl3

@ n00dl3 팁 주셔서 감사합니다! "하지 말아야 할 일 :"섹션에서 설명하려고했습니다. 자유롭게 편집하십시오.
eko

이것이 귀하의 질문에 대답합니까? 비동기 호출에서 응답을 어떻게 반환합니까?
Heretic Monkey

게시물이 이미 답변에 크레딧이있는 @HereticMonkey
eko

@eko 그리고? 이 질문 이 중복 되는 문제 를 줄 입니까?
Heretic Monkey

답변:


117

이유:

그 이유 undefined는 비동기 작업 을하고 있기 때문입니다 . getEventList방법 을 완료하는 데 약간의 시간이 걸립니다 (대부분 네트워크 속도에 따라 다름).

http 호출을 살펴 보겠습니다.

this.es.getEventList()

실제로 http 요청을 ( "실행") 한 후에 는 응답을 기다릴subscribe 것 입니다. 기다리는 동안 자바 스크립트는이 코드 아래의 줄을 실행하고 동기 할당 / 작업이 발생하면 즉시 실행합니다.

따라서에 가입 getEventList()하고 응답을 기다린 후

console.log(this.myEvents);

라인이 즉시 실행됩니다. 그리고 그것의 가치는 undefined응답이 서버에서 도착하기 전 (또는 처음에 초기화 한 것에 대한 것입니다)입니다.

다음을 수행하는 것과 유사합니다.

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

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


해결책:

그렇다면이 문제를 어떻게 극복할까요? subscribe메서드 인 콜백 함수를 사용합니다 . 데이터가 서버에서 도착 subscribe하면 응답과 함께 내부에 있기 때문 입니다.

따라서 코드를 다음과 같이 변경하십시오.

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

잠시 후 응답을 인쇄합니다.


해야 할 일 :

응답을 로깅하는 것 외에 다른 많은 작업이있을 수 있습니다. subscribe데이터가 도착하면 콜백 내부 ( 함수 내부)에서 이러한 모든 작업을 수행해야 합니다.

언급해야 할 또 다른 사항은 Promise백그라운드 에서 온 경우 then콜백 subscribe이 Observable에 해당한다는 것 입니다.


하지 말아야 할 것 :

비동기 작업을 동기화 작업으로 변경해서는 안됩니다. 비동기 작업이있는 이유 중 하나는 사용자가 해당 기간 동안 다른 작업을 수행 할 수있는 동안 작업이 완료 될 때까지 기다리지 않도록하기 위해서입니다. 비동기 작업 중 하나를 완료하는 데 3 분이 걸린다고 가정합니다. 비동기 작업이 없으면 인터페이스가 3 분 동안 멈 춥니 다.


추천 자료 :

이 답변에 대한 원래 크레딧은 다음과 같습니다. 비동기 호출에서 응답을 어떻게 반환합니까?

그러나 angular2 릴리스에서는 typescript와 observable에 대해 소개되었으므로이 답변은 observable을 사용하여 비동기 요청을 처리하는 기본 사항을 다루기를 바랍니다.


3
질문자가 게시물과 똑같은 시간에 질문에 답변했을 때 .. 블로그 게시물을 대신 작성하기에 좋은 곳입니다
Jonas Praem

3
@JonasPraem 사실이지만 Stackoverflow에 대한 지식을 공유하는 데 아무런 문제가 없습니다. 표의 수를 보시다시피, 여기 많은 사람들에게 도움이되었으며 계속 그렇게 할 것입니다.
Amit Chigadani

11

angular / javascript에서 http 호출을 만드는 것은 비동기 작업입니다. 따라서 http 호출을 할 때 새 스레드를 할당하여이 호출을 완료하고 다른 스레드로 다음 줄 실행을 시작합니다. 그것이 당신이 정의되지 않은 가치를 얻는 이유입니다. 이 문제를 해결하려면 아래를 변경하십시오.

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


3

Observable은 게으 르기 때문에 값을 얻으려면 구독해야합니다. 코드에서 올바르게 구독했지만 동시에 'subscribe'블록 외부에 출력을 기록했습니다. 그것이 '정의되지 않은'이유입니다.

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

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

따라서 구독 블록 안에 기록하면 응답이 제대로 기록됩니다.

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

3

여기서 문제는 블록 밖에서 수행하는 동안 비동기 블록 this.myEvents으로 초기화 subscribe()한다는 console.log()것입니다 subscribe(). 그래서 초기화 console.log()되기 전에 호출 this.myEvents됩니다.

console.log () 코드와 subscribe () 내에서 이동하면 완료됩니다.

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

2

각도 프로세스가 async이므로 결과는 정의되지 않습니다. 다음과 같이 시도 할 수 있습니다.

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

1

또한 응답을 json 출력에 매핑해야합니다. 그렇지 않으면 일반 텍스트를 반환합니다. 다음과 같이합니다.

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

서비스의 데이터가 위의 구독 서비스 호출에서 설정되기 전에 여기에있는 값이 기록되므로 정의되지 않습니다. 따라서 ajax 호출이 완료 될 때까지 기다렸다가 응답 데이터에서 데이터를 설정해야합니다.

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

여기에서 myEvents 변수에 데이터가 설정 될 때 로그를 생성 할 subscribe 메서드 내부에 콘솔 로그를 만듭니다.

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

0

이 방법을 시도해 볼 수 있습니다.

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');
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.