web-dev-qa-db-ja.com

Observableとasync / awaitを使用するのは良い習慣ですか?

Observableを返すangular 2共通HTTPを使用していますが、ネストされたObservable呼び出しを使用すると、コードがメッシュを好むという問題に直面します。

this.serviceA.get().subscribe((res1: any) => {
   this.serviceB.get(res1).subscribe((res2: any) => {
       this.serviceC.get(res2).subscribe((res3: any) => {

       })
   })
})

今、私はそれを避けるためにasync/awaitを使いたいが、async/awaitはPromiseでのみ動作する。 ObservableはPromiseに変換できることを知っていますが、私が知っているように、それは良い習慣ではありません。だから私はここで何をすべきですか?

ところで、誰かが私にasync/awaitでこれを解決するためのサンプルコードを与えることができれば素晴らしいでしょう:D

25
Thach Huynh

コードで行いたいように、オブザーバブルを順番に連鎖する

コード例に関して、Observableをチェーンする(前の出力の後に別のトリガーをトリガーする)場合は、この目的でflatMap(またはswitchMap)を使用します。

this.serviceA.get()
  .flatMap((res1: any) => this.serviceB.get())
  .flatMap((res2: any) => this.serviceC.get())
  .subscribe( (res3: any) => { 
    .... 
  });

ObservableとPromisesがそもそも防止に役立つはずだったコールバックの地獄を避けるために、これは物事を明確にし、あなたを助けるので、これはネストに比べてより良い習慣です。

また、switchMapの代わりにflatMapを使用することを検討してください。最初のリクエストが新しい値を発行した場合、基本的に他のリクエストを「キャンセル」できます。たとえば、残りをトリガーする最初のObservableがボタンのクリックイベントである場合に使用すると便利です。

さまざまなリクエストを順番に待つ必要がない場合は、forkJoinまたはZipを使用してそれらを一度に開始できます。 @ Dan Macak answer's 詳細およびその他の洞察。


Angular 'async' pipeとObservablesはうまく連携します

ObservablesとAngularに関しては、コンポーネントコードでObservableにサブスクライブする代わりに、Angularテンプレートで| asyncパイプを完全に使用して、このObservableによって発行された値を取得できます。


ES6 async/awaitとObservablesの代わりのPromises?

observableを直接使用していると感じていない場合は、Observableで.toPromise()を使用してから、async/await命令を使用できます。

Observableが結果を1つだけ返すことになっている場合(基本的なAPI呼び出しの場合)、ObservableはPromiseとまったく同等と見なすことができます。

ただし、Observableがすでに提供しているすべてのものを考慮すると、それを行う必要があるかどうかはわかりません(読者へ:啓発的な反例を歓迎します!)トレーニング演習として、できる限りObservablesを使用することをお勧めします。


それに関するいくつかの興味深いブログ記事(および他にもたくさんあります):

https://medium.com/@benlesh/rxjs-observable-interop-with-promises-and-async-await-bebb05306875

ToPromise関数は、実際には「演算子」ではなく、ObservableにサブスクライブしてPromiseにラップするRxJS固有の手段であるため、実際には少し注意が必要です。 Observableが完了すると、PromiseはObservableの最後に発行された値に解決されます。つまり、Observableが値「hi」を発行し、10秒待ってから完了すると、返されたプロミスは10秒待ってから「hi」を解決します。 Observableが完了しない場合、Promiseは解決しません。

注:toPromise()の使用は、async-awaitなど、Promiseを期待するAPIを扱っている場合を除き、アンチパターンです

(エンファシス鉱山)


リクエストした例

ところで、誰かが私にasync/awaitでこれを解決するためのサンプルコードを与えることができれば素晴らしいでしょう:D

本当にしたい場合の例(おそらくいくつかの間違いがあり、今すぐチェックできないため、お気軽に修正してください)

// Warning, probable anti-pattern below
async myFunction() {
    const res1 = await this.serviceA.get().toPromise();
    const res2 = await this.serviceB.get().toPromise();
    const res3 = await this.serviceC.get().toPromise();
    // other stuff with results
}

すべての要求を同時に開始できる場合は、await Promise.all()の方が効率的です。これは、呼び出しが相互の結果に依存しないためです。 (forkJoin Observablesと同様)

async myFunction() {
    const promise1 = this.serviceA.get().toPromise();
    const promise2 = this.serviceB.get().toPromise();
    const promise3 = this.serviceC.get().toPromise();

    let res = await Promise.all([promise1, promise2, promise3]);

    // here you can promises results,
    // res[0], res[1], res[2] respectively.
}
41
Pac0

@ Pac0はすでにさまざまなソリューションについて詳しく説明しているので、少しだけ異なる角度を追加します。

プロミスとオブザーバブルの混合

個人的にはPromiseとObservablesを混在させない-Observablesで非同期待機を使用しているときに得られるものです。

  • 約束は常に非同期であり、オブザーバブルは必ずしもそうではありません
  • Promiseは1つの値、Observable 0、1、または多くを表します
  • Promiseの使用は非常に限られているため、できません。それらをキャンセルします(ESの次の提案はさておき)、Observableの使用ははるかに強力です(たとえば、複数のWS接続を管理できます。Promisesで試してみてください)
  • APIは大きく異なります

AngularでのPromiseの使用

両方を使用することは時々有効ですが、特にwith Angular RxJSを可能な限り使用することを検討すべきだと思います。その理由は:

  • Angular AP​​Iの大部分はObservables(ルーター、http ...)を使用しているため、RxJSを使用することにより、1種類のストリームに対応しますRxJSが提供する失われた可能性を補いながら、常にPromisesに変換する必要があります。
  • Angularには強力なasyncパイプがあり、サーバーからのデータストリームを中断することなく、アプリケーションのデータフロー全体を作成し、フィルタリング、結合、および任意の変更を行うことができます単一の必要なしにtheningまたはサブスクライブの場合。このように、データをアンラップしたり、いくつかの補助変数に割り当てる必要はありません。データはサービスからObservablesを介してテンプレートに直接流れるだけで、とても美しいです。

ただし、Promise can be shineの場合もあります。たとえば、rxjsTypeScriptタイプに欠けているのは、singleの概念です。他の人が使用するAPIを作成している場合、Observableを返すだけでは十分ではありません。1つの値を受け取りますか、多くの値を受け取りますか、それとも完全に完了しますか。それを説明するコメントを書く必要があります。一方、Promiseはこの場合、より明確な契約を結んでいます。常に1つの値で解決されるか、エラーで拒否されます(もちろん永遠にハングアップしない限り)。

通常、プロジェクトにPromisesまたはObservablesのみを含める必要はありません。何かが完了したことを値で表現したい場合(ユーザーの削除、ユーザーの更新)、それを統合せずに対応したい場合いくつかのストリーム、Promiseはそうするより自然な方法です。また、async/awaitを使用すると、コードをシーケンシャルに記述できるため、コードを大幅に簡素化できます。したがって、着信値の高度な管理が必要でない限り、Promiseをそのまま使用できます。


例に戻る

したがって、私の推奨事項は、RxJSとAngulaの両方の力を取り入れる rです。あなたの例に戻ると、次のようにコードを書くことができます(@Vayrexへのアイデアの功績):

this.result$ = Observable.forkJoin(
  this.serviceA.get(),
  this.serviceB.get(),
  this.serviceC.get()
);

this.result$.subscribe(([resA, resB, resC]) => ...)

このコードは3つのリクエストを実行し、それらのリクエストObservablesがすべて完了すると、forkJoinへのサブスクリプションコールバックにより配列で結果が取得され、前述のように手動でサブスクライブできます(例のように) )またはテンプレートでresult$およびasyncパイプを使用して宣言的にこれを行います。

Observable.Zipを使用すると、ここで同じ結果が得られます。forkJoinZipの違いは、前者は内部Observableの最後の値のみを出力し、後者は内部Observableの最初の値を組み合わせ、次に、2番目の値など.


編集:以前のHTTPリクエストの結果が必要なので、@ Pac0の回答でflatMapアプローチを使用します。

12
Dan Macák