Observable / 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 2017

@ 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データが到着したら、コールバック内(関数内)でこれらすべての操作を行う必要があります。

もう1つ言及すべきことはPromisethenバックグラウンドから来た場合、コールバックsubscribeはオブザーバブルに対応することです。


してはいけないこと:

非同期操作を同期操作に変更しようとするべきではありません(できません)。非同期操作がある理由の1つは、その期間内に他のことができる間、ユーザーが操作の完了を待たないようにすることです。非同期操作の1つが完了するまでに3分かかるとします。非同期操作がない場合、インターフェースは3分間フリーズします。


推奨読書:

この回答の元のクレジットは次のとおりです。非同期呼び出しから応答を返すにはどうすればよいですか?

しかし、angular2リリースでは、typescriptとobservablesが導入されたため、この回答で、observableを使用した非同期要求の処理の基本について説明できます。


3
質問の回答者が投稿の正確なタイミングで質問に回答したとき。これは、代わりにブログ投稿を停止して書くのに適した場所です
Jonas Praem

3
@JonasPraem正しいですが、Stackoverflowに関する知識を共有することに問題はありません。投票数を見るとわかるように、投票はここで多くの人々を助けましたし、今後もそうするでしょう。
アミット茅ヶ谷

11

angular / javascriptでhttp呼び出しを行うことは非同期操作です。したがって、http呼び出しを行うと、新しいスレッドを割り当ててこの呼び出しを終了し、別のスレッドで次の行の実行を開始します。そのため、未定義の値を取得しています。これを解決するには、以下の変更を行ってください

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

5

あなたは使用することができますasyncPipeをあなたが唯一のテンプレートでmyEventsを使用している場合。

ここでasyncPipeとAngular4 HttpClientの例を使用した


1
そして、この実装の1が契約を解除する必要はありませんで(!):medium.com/@sub.metu/...
サンジェイ・ファルコン

@SanJayFalconこれはhttpリクエストであるため、サブスクリプションを解除する必要はありません。
AJT82

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.myEventssubscribe()行っていることです。したがって、初期化される前に呼び出されます。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 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.