Як я можу повернути відповідь від спостережуваного / http / async-дзвінка у кутовому?


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

Я перевірив Як повернути відповідь від асинхронного дзвінка? повідомлення, але не вдалося знайти рішення


2
це було б гарним моментом наголосити на тому, що неможливо перетворити асихронну операцію в синхронну.
n00dl3

@ n00dl3 Дякую за пораду! Я намагався пояснити в розділі «Що не слід робити:». Не соромтесь редагувати це.
еко


@HereticMonkey це повідомлення вже зараховано у відповідь
еко

@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 ми ознайомилися з машинописом та спостережними документами, тому ця відповідь, сподіваємось, охоплює основи обробки асинхронного запиту із спостережними.


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

Спостереження ліниві, тому вам доведеться підписатися, щоб отримати значення. Ви підписали його належним чином у своєму коді, але водночас увійшли до виходу поза блоком "підписатися". Ось чому це "невизначено".

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 (), а також підписатись (), і ви закінчите.

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

2

Результат не визначений, оскільки кутова асинхроніка процесу. ви можете спробувати як нижче:

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.

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');
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.