Как мне вернуть ответ от вызова Observable / http / async в angular?

110

У меня есть служба, которая возвращает наблюдаемый объект, который выполняет HTTP-запрос на мой сервер и получает данные. Я хочу использовать эти данные, но всегда получаю 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!
  }
}

Я проверил Как мне вернуть ответ от асинхронного вызова? опубликовать, но не смог найти решение

EKO
источник
2
это было бы хорошим моментом, чтобы подчеркнуть тот факт, что невозможно преобразовать асинхронную операцию в синхронную.
n00dl3
@ n00dl3 Спасибо за подсказку! Я пытался объяснить в разделе «Чего нельзя делать:». Не стесняйтесь редактировать это.
eko
Отвечает ли это на ваш вопрос? Как мне вернуть ответ от асинхронного вызова?
Обезьяна-еретик,
@HereticMonkey, этот пост уже указан в ответе
eko
@eko А? Делает ли это вопрос менее дублирующимся?
Обезьяна-еретик

Ответы:

117

Причина:

Причина в том, undefinedчто вы выполняете асинхронную операцию. Это означает, что для завершения getEventListметода потребуется некоторое время (в основном в зависимости от скорости вашей сети).

Итак, давайте посмотрим на вызов http.

this.es.getEventList()

После того, как вы действительно сделаете ("выстрелите") ваш http-запрос, subscribeвы будете ждать ответа. Во время ожидания javascript выполнит строки под этим кодом, и если он обнаружит синхронные назначения / операции, он выполнит их немедленно.

Итак, подписавшись на рассылку 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наблюдаемым.


Чего делать не следует:

Вы не должны пытаться изменить асинхронную операцию на операцию синхронизации (не то чтобы вы могли). Одна из причин, по которой у нас есть асинхронные операции, заключается в том, чтобы не заставлять пользователя ждать завершения операции, пока он может делать другие вещи в этот период времени. Предположим, что выполнение одной из ваших асинхронных операций занимает 3 минуты, если бы у нас не было асинхронных операций, интерфейс завис бы на 3 минуты.


Предлагаемое чтение:

Первоначальная заслуга в этом ответе заключается в следующем: Как мне вернуть ответ от асинхронного вызова?

Но с выпуском angular2 мы познакомились с машинописным текстом и наблюдаемыми объектами, поэтому мы надеемся, что этот ответ охватывает основы обработки асинхронного запроса с помощью наблюдаемых объектов.

EKO
источник
3
Когда задающий вопрос отвечает на вопрос в то же время, что и пост ... Это хорошее место, чтобы остановиться и вместо этого написать сообщение в блоге
Йонас Прем
3
@JonasPraem Верно, но нет ничего плохого в том, чтобы поделиться знаниями о Stackoverflow. Как вы видите количество голосов, это действительно помогло многим людям здесь, и будет продолжать это делать.
Амит Чигадани
11

Выполнение http-вызова в angular / javascript - это асинхронная операция. Поэтому, когда вы выполняете http-вызов, он назначает новый поток, чтобы завершить этот вызов и начать выполнение следующей строки с другим потоком. Вот почему вы получаете неопределенное значение. поэтому внесите изменения ниже, чтобы решить эту проблему

this.es.getEventList()  
      .subscribe((response)=>{  
       this.myEvents = response;  
        console.log(this.myEvents); //<-this become synchronous now  
    });
РАВИ ПАТЕЛ
источник
5

Вы можете использовать asyncPipe если используете myEvents только в шаблоне.

Here пример с asyncPipe и Angular4 HttpClient пример

Климент Ру
источник
1
И (!) С этой реализацией не нужно отказываться от подписки: medium.com/@sub.metu/…
San Jay Falcon
@SanJayFalcon Поскольку это HTTP-запрос, отписки нет.
AJT82
3

Observables ленивы, поэтому вам нужно подписаться, чтобы получить значение. Вы правильно подписали его в своем коде, но одновременно регистрировали вывод вне блока «подписаться». Вот почему он «не определен».

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'
    });
}
Kooldandy
источник
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

Результат не определен, потому что angular обрабатывает 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;})
}
luukgruijs
источник
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.

ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
     // This prints the value from the response
    console.log(this.myEvents)
        }); 
  }
Hitech Hitesh
источник
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');
    }
Harshit
источник