nakamurakko’s blog

仕事で覚えたこと、勉強したことを自分のメモ代わりに書いていこうかなと。

Observable を返すメソッドが jorkJoin ですり抜けないようにする

環境

  • Angular 9.1.11
  • rxjs 6.5.5
  • Azure Functions (.NET Core 3.1、データ要求先として)

準備

Azure Functionsで1つ関数を用意する。

まずはデータ用クラスを用意して、

class NumberValue
{
    public int Value { get; set; }
}

そのデータ用クラスの配列をJSON形式で返す関数を用意する。

[FunctionName("GetNumbers")]
public static async Task<IActionResult> GetNumbers(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
    ILogger log)
{
    var result = new List<NumberValue>();

    await Task.Run(() =>
    {
        for (var i = 0; i < 10; i++)
        {
            result.Add(new NumberValue() { Value = i });
        }
    });

    return new OkObjectResult(JsonConvert.SerializeObject(result));
}

Serviceの実装

では、そのデータを要求して受け取る処理を作る。

Azure Functionsと同じ型をinterfaceで用意する。

export class NumberValue {
  Value: number;
}

ServiceにGetNumbersを呼び出してObservableを返すメソッドを作成する。

import { NumberValue } from './data-types/number-value';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class SampleService {

  constructor(private http: HttpClient) { }

  /**
   * NumberValueの配列を返す。
   */
  public getNumbers(): Observable<Array<NumberValue>> {
    let result: Subject<Array<NumberValue>> = new Subject<Array<NumberValue>>();

    this.http.get('https://yoursitename.azurewebsites.net/api/GetNumbers')
      .subscribe(response => {
        result.next(response as Array<NumberValue>);

        // forkJoinなどで待ち受ける場合はcomplete()を呼び出す必要がある。
        result.complete();
      });

    return result;
  }
}

参考にしたのはこちら。

HttpClient.getでObservable<any>が返ってくるので、編集してからメソッドの戻り値に使いたい場合はSubjectに詰め込んで返してあげると良い。

forkJoinのDescriptionには「complete()を待つ」と書いてあるので、戻り値として使うSubjectのcomplete()を呼び出しておけば、forkJoinで使ってもすり抜ける事が無くなる。

(HttpClient.getの戻り値を編集しない場合はSubjectを書く必要が無く、return this.http.get('https://yoursitename.azurewebsites.net/api/GetNumbers') as Observable<Array<NumberValue>>;で大丈夫。)

使ってみる

実装したServiceをComponentで呼び出してみる。

import { SampleService } from './../sample.service';
import { Component, OnInit } from '@angular/core';
import { forkJoin } from 'rxjs';

@Component({
  selector: 'app-sample',
  templateUrl: './sample.component.html',
  styleUrls: ['./sample.component.css']
})
export class SampleComponent implements OnInit {
  responseData: string = '';

  constructor(private sampleService: SampleService) { }

  ngOnInit(): void {
  }

  public onHttpGet() {
    this.responseData = '';

    // forkJoinで2回呼び出して、
    forkJoin(
      this.sampleService.getNumbers(),
      this.sampleService.getNumbers()
    )
      .subscribe(response => {
        // subscribeでまとめて受ける。
        this.responseData = JSON.stringify(response);
      });
  }
}

メソッドを呼び出して、Azure Functionsのアクセス結果が2回分出ていることを確認できた。

f:id:nakamurakko:20200714191228p:plain