[Angular/TypeScript(JavaScript)] 非同期処理/待ち合わせ処理のまとめ (Observable/subscribe/forkJoin/Promise/async/await/then)

  • Angular等のTypeScriptベースのフレームワークでフロントエンドを開発する際には、待ち合わせ処理が度々問題となる
  • 外部通信が基本的に非同期処理であるため、返り値を加工するにはひと手間加える必要がある

前提知識

同期/非同期/待ち合わせ処理とは?

プログラムを書く前にどれが最適化か?を判断する必要がある(書き方が変わってくる)

  • 非同期処理

    • 実行完了を待たずに次の処理を並列で開始する
    • メリット
      • 処理の高速化を図れる。
      • 一部の処理でエラーが出ても、他の処理は問題無く動く
      • 処理完了までユーザーを待たせない
    • デメリット
      • 返り値を使って次の処理を行いたい時に問題が発生する
        (返り値無しの状態で次の処理を実行してしまう)
  • 同期処理

    • 処理完了まで他の処理をストップする
    • メリット
      • 処理を順に実行できるため、プログラムを書きやすい
    • デメリット
      • 処理完了までユーザーを待たせる事になる
  • 待ち合わせ処理

    • ある条件が満たされるまで処理を待たせる
      • ex. 返り値が戻ってくるまである処理の開始を待たせる
    • 同期処理とイコールではない
    • 複数の処理群としてみると非同期でOKだが、個々の処理は待ち合わせて順番に実行させたいケースがある
      • ユーザーが処理の完了を待つ必要がない → 非同期でOK
      • データ取得後に加工したい → 待ち合わせ処理が必要
  • TypeScript(JavaScript)においては、以下のような処理が非同期で実行されることを考慮する必要がある

    • API通信
    • データベース通信
    • その他の重い処理全般

TypeScript(JS)の待ち合わせ処理問題とは

上述の非同期で動く特性から、初級者は以下の問題でハマることが多い

  • TypeScript(JS)では、プログラムが書かれた順に動くとは限らない

    • 基本的に非同期処理で動く
      • 非同期処理は重い処理の終了を待たずに、次の処理を進められるので、高速化という意味では有効だが、困るシーンもある
  • よくあるケース

    • 外部APIと通信してデータを取得する場合、その戻り値が帰ってくる前に次の処理に進んでしまう。そのため、以降の処理がデータ無しで行われてしまう
    • 正しく外部にリクエストできている筈が、データ=undefinedと出力されてしまう
      • この辺りはChromeの開発モードで出力順を見ると理解し易い
  • 回避策: 待ち受け処理/同期処理を実現する(返り値が来た後に次の処理を実行させるように書き換える)
    • 書き方は色々ある
      1. Observable, subscibeを活用して待ち合わせ処理を実現する
      2. Observable, forkJoin()を活用して待ち合わせ処理を実現する
      3. aync/awaitを利用して同期処理化する
      4. Promise, then()を活用して同期処理化する

Observable型を使って待ち合わせ処理を実現するパターン

  • Angularでは適したケースであれば、基本的にPromiseよりもObservableが推奨されている

  • service側の返り値をobservable型に定義、component側で.subscribe()で受け取り、その中に処理を書き込む

  • ポイント

    • Observableでも待ち合わせ処理は実現できるが、同期処理はできない
    • 同期処理についてはPromiseを使う
    • ストリームを扱える

1. Observable & subscribe()

  • Service側

    • 返り値をObservable型で定義
  • Component側

    • .subscribe()で返り値を受け取る
  • sample.service.ts

    • Observable型で返り値を返す
      1
      2
      3
      4
      5
      6
      getmethod(){
      return observable = new Observable<number>(observer => {
      // 外部APIとの通信処理

      });
      }
  • sample.component.ts

    • 書式:observable.subscribe()
1
2
3
4
5
6

this.sampleService.getMethod().subscribe(value => {

// 返り値を利用する処理をここに書く

});

2. Observable & forkJoin()

  • forkJoin()を活用すると、Observableの全ての処理の完了を待って次の処理を実行できる

    • ユースケース
      • 複数回外部APIにリクエストを実行して、全てのデータが帰ってきてから加工
  • 書式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 複数の外部リクエストを実行してobservable型で受けとる
    // 配列hogehogeの中身を一つずつ引数として渡して実行する例
    const observable = hogehoge.map(hoge => {
    return this.sampleService.getData(hoge).pipe(catchError(e => observableOf({"error": e})))
    });

    // 待ち合わせ処理
    forkJoin(observables).subscribe( response => {
    // 以降に処理を記載
  • 活用例は以下の記事に記載した


Promise型を使って同期処理化するパターン

3. async/awaitを活用する

  • asyncをつけた関数の返り値はPromise型となる

  • asyncをつけた関数はawaitで待てる

  • await

    • Promiseの値が取り出されるまで待つ
  • async

    • awaitキーワードを使っている関数の頭に付ける必要がある
  • import必要?

  • ポイント

    • 同期処理化したい関数の宣言時:頭にasyncを付ける
    • 同期処理化したい関数の実行時:awaitを付ける
    • awaitはasyncを付けた関数内でしか実行できない

Serviceで外部API通信を実行 ⇒ Component側の変数に受け取る例

  • sample.component.ts
    • awaitを付けた関数の処理を待つ
      1
      2
      3
      4
      5
      6
      let result; // 返り値を受け取る変数
      async ngOnInit() {
      // Service経由で外部APIからデータを受け取る
      this.result = await this.sampleService.getMethod()
      console.log(this.getData);
      }
  • sample.service.ts
    • 関数の頭にasyncを付ける
      1
      2
      async getMethod(){
      }

複数の関数を順に実行する例

1
2
3
4
5
6
7
8
9
10
11
12
13
// プログラムの大筋
async function main() {
const x = await getX()
const y = await getY()
console.log(x + y)
}
async function getX() {
return 1
}
async function getY() {
return 2
}
main()

アロー関数の場合はasyncをつける箇所が異なる

  • Angularの場合は基本的にアロー関数が推奨されているのでこの書き方も覚えた方が良い
1
2
3
4
5
6
7
8
// functionによる関数宣言
async function sampleFunc() {
// 処理内容
}
// アロー関数による関数宣言
const sampleFunc = async() => {
// 処理内容
}

4. Promise, .then()を活用する

参考

関連記事

非同期処理参考

@EventEmitter @Input @Output @ViewChild ACM AMP API Gateway AR AR.js AR.js Studio AWS AWS Amplify AWS Budgets AWS Cost Explorer AWS SDK for JavaScript AddThis Adobe XD Alexa Amazon CloudWatch Amazon Honycode Amazon SNS Android Angular Angular Material AngularFire AppSync Augury C CDN CI/CD Cloud Craft Cloud9 CloudFormation CloudFront CloudTrail Cognito Cost Anomaly Detection Cubase ai Cubasis LE DTM Disqus DynamoDB Elgato HD 60S Firebase Firebase Hosting Former2 Github Github Actions Github.com GithubEnterprise GithubPages Google Chrome Google Cloud Shell GraphQL Hexo Hosting IAM Ionic JSON JavaScript LadioCast Logging LowCode MFA MS Authentication MacBook Pro 16 Mind Node NETDUETTO Netflix Party Netlify Network NoCode Observable PO PdM Promise RPA ReactiveForm Recognition Route53 S3 SAM(Serverless Application Model) SAR SSL SYNCROOM Schematics ScrumInc Serverless Service Siri Sitemap Spark AR Steinberg UR44C Teams Touch Cast Studio Treetable TypeScript UI UI Bakery WAF WAFv2 WEB Page Dev WEB会議 WebAR WebSocketAPI Webhook Windows Power Automate Wireshark aot async/await aws config cloud9 display.land draw.io e2e test filter() forkJoin() google search console hexo-generator-amp iOS iPad Pro iPhone icarus map() mat input mat tree mat-checkbox mat-input mat-selection-list mmhmm ngFor plantUML popIn Aladdin2 then() vscode ”global is not defined” らくがきAR アジャイル アジャイル開発 クロスプラットフォーム ショートカット スクラム スクラム開発 テレワーク ファイル操作 ブラウザ型IDE プロダクトオーナー プロダクトマネージャー プロトタイピング リモートセッション 共同開発 双方向データバインディング 待ち合わせ処理 認定スクラムマスター 静的WEB Hosting 静的WEBサイトHosting
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×