[angular]RxJSを使用して複数のHttp要求を処理する

6494 ワード

あるページにアクセスすると、複数のAPIアドレスからデータを取得して表示する必要がある場合があります.複数の非同期データ要求を管理することは困難ですが、Angular HttpサービスとRxJSライブラリが提供する機能を利用して上記の機能を実現できます.複数のリクエストを処理するには、シリアルまたはパラレルを使用する方法がいくつかあります.
基礎知識
mergeMap
mergeMapオペレータは、内部のObservableオブジェクトから値を取得し、親ストリームオブジェクトに返すために使用します.
  • Observableオブジェクト(jsBin)
  • をマージ
    const source = Rx.Observable.of('Hello');
    //map to inner observable and flatten
    const example = source.mergeMap(val => Rx.Observable.of(`${val} World!`));
    
    const subscribe = example.subscribe(val => console.log(val)); //output: 'Hello World!'
    

    上記の例では、2つのObservableタイプが含まれています.
  • ソースObservableオブジェクト-すなわちsourceオブジェクト
  • 内部Observableオブジェクト-すなわちRx.Observable.of(${val} World!)オブジェクト
  • 内部のObservableオブジェクトから値が発行された場合にのみ、ソースObservableオブジェクトから出力された値がマージされ、マージされた値が最終的に出力されます.
    forkJoin
    forkJoinはRxバージョンですPromise.all()、すなわち、すべてのObservableが完了するまで、値が一度に返されないことを示す.
  • 複数のObservableオブジェクト(jsBin)
  • をマージ
    const getPostOne$ = Rx.Observable.timer(1000).mapTo({id: 1});
    const getPostTwo$ = Rx.Observable.timer(2000).mapTo({id: 2});
    
    Rx.Observable.forkJoin(getPostOne$, getPostTwo$).subscribe(
      res => console.log(res) // [{id: 1}, {id: 2}]
    ); 
    

    Httpリクエストの処理
    まず、Angular Httpサービスの簡単な例を見てみましょう.
    import { Component, OnInit } from '@angular/core';
    import { Http } from '@angular/http';
    
    import 'rxjs/add/operator/map';
    
    @Component({
      selector: 'app-root',
      template: `
        

    HttpModule Demo

    ` }) export class AppComponent implements OnInit { constructor(private http: Http) { } ngOnInit() { this.http.get('https://jsonplaceholder.typicode.com/users') .map(res => res.json()) .subscribe(users => console.log(users)); } }

    上記の例では、注入に依存する方法で注入します.http
    サービス、そしてngOnInit()
    メソッドでhttpオブジェクトを呼び出すget()
    メソッドを使用してデータを取得します.この例は簡単で、1つのリクエストのみを処理します.次に、2つのリクエストをどのように処理するかを見てみましょう.
    MapとSubscribe
    次のリクエストを送信する場合、前のリクエストのデータに依存する必要がある場合があります.すなわち、前の要求のコールバック関数で対応するデータを取得し、別のHTTP要求を開始する必要がある.
    import { Component, OnInit } from '@angular/core';
    import { Http } from '@angular/http';
    import 'rxjs/add/operator/map';
    
    @Component({
      selector: 'app-root',
      template: `
        

    {{username}} Detail Info

    {{user | json}} ` }) export class AppComponent implements OnInit { constructor(private http: Http) { } apiUrl = 'https://jsonplaceholder.typicode.com/users'; username: string = ''; user: any; ngOnInit() { this.http.get(this.apiUrl) .map(res => res.json()) .subscribe(users => { let username = users[6].username; this.http.get(`${this.apiUrl}?username=${username}`) .map(res => res.json()) .subscribe( user => { this.username = username; this.user = user; }); }); } }

    上記の例では、まずhttps://jsonplaceholder.typicode.com/users
    アドレスはすべてのユーザーの情報を取得し、指定したユーザーのusername
    ユーザーの詳細をさらに取得します.機能は実現しましたが、より良い解決策はありますか?答えはありますが、RxJSライブラリで提供できますmergeMap
    オペレータは、上記のプロセスを最適化します.
    mergeMap
    import { Component, OnInit } from '@angular/core';
    import { Http } from '@angular/http';
    import 'rxjs/add/operator/map';
    import 'rxjs/add/operator/mergeMap';
    
    @Component({
      selector: 'app-root',
      template: `
        

    {{username}} Detail Info

    {{user | json}} ` }) export class AppComponent implements OnInit { constructor(private http: Http) { } apiUrl = 'https://jsonplaceholder.typicode.com/users'; username: string = ''; user: any; ngOnInit() { this.http.get(this.apiUrl) .map(res => res.json()) .mergeMap(users => { this.username = users[6].username; return this.http.get(`${this.apiUrl}?username=${this.username}`) .map(res => res.json()) }) .subscribe(user => this.user = user); } }

    上記の例ではmergeMap
    オペレータは、ネストされたサブスクリプションの問題を解決します.最後に、複数の並列Httpリクエストをどのように処理するかを見てみましょう.
    forkJoin
    次の例では、forkJoin
    オペレータ.Promisesに詳しい方は、このオペレータとPromise.all()
    実装される機能は似ています.forkJoin
    オペレータは、Observableオブジェクトのリストを受信し、並列に実行します.リストのObservableオブジェクトが値を発行すると、forkJoinオペレータが返すObservableオブジェクトは、新しい値、すなわち、すべてのObservableオブジェクトの出力値を含むリストを発行します.具体例は次のとおりです.
    import { Component, OnInit } from '@angular/core';
    import { Http } from '@angular/http';
    
    import { Observable } from 'rxjs/Observable';
    import 'rxjs/add/operator/map';
    import 'rxjs/add/observable/forkJoin';
    
    @Component({
      selector: 'app-root',
      template: `
        

    Post Detail Info

    • {{post1 | json}}
    • {{post2 | json}}
    ` }) export class AppComponent implements OnInit { constructor(private http: Http) { } apiUrl = 'https://jsonplaceholder.typicode.com/posts'; post1: any; post2: any; ngOnInit() { let post1 = this.http.get(`${this.apiUrl}/1`); let post2 = this.http.get(`${this.apiUrl}/2`); Observable.forkJoin([post1, post2]) .subscribe(results => { this.post1 = results[0]; this.post2 = results[1]; }); } }

    話があるんだ
    mergeMap以外に、RxJSのswitchMapは何の役に立ちますか?
    switchMapオペレータは、ソースObservableオブジェクトから発行される値をマッピング処理するために使用されます.新しいObservableオブジェクトが表示されると、新しいObservableオブジェクトが新しい値を発行した後、前の未処理のObservableオブジェクトがキャンセルされます.
    使用例:JSBin
    var source = Rx.Observable.fromEvent(document.body, 'click');
    var example = source.switchMap(e => Rx.Observable.interval(100).take(3));
    
    example.subscribe({
        next: (value) => { console.log(value); },
        error: (err) => { console.log('Error: ' + err); },
        complete: () => { console.log('complete'); }
    });
    

    例marble図:
    source : -----------c--c-----------------...
            concatMap(c => Rx.Observable.interval(100).take(3))
    example: -------------0--0-1-2-----------...
    

    以上のコードが実行された後、コンソールの出力結果:
    0
    0
    1
    2
    

    実際にHttpサービスを利用するシーンでは、AutoComplete機能を実現するなど、switchMap
    オペレータは、不要なHttpリクエストをキャンセルします.