私は現在、Angularのrespを置き換えることの賛否両論を評価しています。 Observable
とPromise
を使用して、より直感的なコードスタイルを取得できるように、RxJSのasync
を単純なawait
とともに使用します。
典型的なシナリオの1つ:ngOnInit
内にデータをロードします。 Observables
を使用して、次のことを行います。
_ngOnInit () {
this.service.getData().subscribe(data => {
this.data = this.modifyMyData(data);
});
}
_
代わりにPromise
をgetData()
から返し、async
およびawait
を使用すると、次のようになります。
_async ngOnInit () {
const data = await this.service.getData();
this.data = this.modifyMyData(data);
}
_
Angularは「知る」ことはできません。ngOnInit
がasync
になったことは明らかです。これは問題ではないと感じています。私のアプリはまだしかし、OnInit
インターフェースを見ると、関数は明らかにasync
と宣言できることを示唆するような方法で宣言されていません。
_ngOnInit(): void;
_
だから-結論:私がここでやっていることは理にかなっていますか?または、予期しない問題が発生しますか?
以前と同じです。 ngOnInit
はPromiseを返し、呼び出し元はそのPromiseを無視します。つまり、呼び出し元は、メソッド内のすべてが完了するのを待たずに続行します。この特定のケースでは、ビューの構成が完了し、ビューがthis.data
が設定されています。
それは以前と同じ状況です。呼び出し元はサブスクリプションが完了するのを待たず、アプリを起動してからthis.data
が入力されていました。ビューがdata
に依存している場合は、アクセスできないようにngIf
を設定している可能性があります。
個人的には、その影響を認識している限り、これをぎこちない、または悪い習慣だとは思いません。ただし、ngIf
は面倒な場合があります(どちらの方法でも必要になります)。個人的には、この状況を回避できるように、意味のあるルートリゾルバーを使用することにしました。ルートのナビゲートが完了する前にデータが読み込まれ、ビューが読み込まれる前にデータが利用可能であることがわかります。
さて、AngularはngOnInitが非同期になったことを「認識」しません。これは問題ではないと感じています。私のアプリは以前と同じように動作しています。
意味的には問題なくコンパイルされ、期待どおりに実行されますが、_async / wait
_を書くとエラー処理が犠牲になるので、避けるべきだと思います。
何が起こるか見てみましょう。
約束が拒否された場合はどうなりますか:
_public ngOnInit() {
const p = new Promise((resolver, reject) => reject(-1));
}
_
上記は次のスタックトレースを生成します。
_core.js:6014 ERROR Error: Uncaught (in promise): -1
at resolvePromise (zone-evergreen.js:797) [angular]
at :4200/polyfills.js:3942:17 [angular]
at new ZoneAwarePromise (zone-evergreen.js:876) [angular]
at ExampleComponent.ngOnInit (example.component.ts:44) [angular]
.....
_
未処理のエラーがngOnInit
によってトリガーされたことと、問題のあるコード行を見つけるためのソースコードファイルを確認できます。
拒否される_async/wait
_を使用するとどうなりますか。
_ public async ngOnInit() {
const p = await new Promise((resolver, reject) => reject());
}
_
上記は次のスタックトレースを生成します。
_core.js:6014 ERROR Error: Uncaught (in promise):
at resolvePromise (zone-evergreen.js:797) [angular]
at :4200/polyfills.js:3942:17 [angular]
at rejected (tslib.es6.js:71) [angular]
at Object.onInvoke (core.js:39699) [angular]
at :4200/polyfills.js:4090:36 [angular]
at Object.onInvokeTask (core.js:39680) [angular]
at drainMicroTaskQueue (zone-evergreen.js:559) [<root>]
_
どうした?スタックトレースがコンポーネントの外側にあるため、手掛かりはありません。
それでも、promiseを使用して、_async / wait
_の使用を避けたくなるかもしれません。では、setTimeout()
の後にpromiseが拒否された場合にどうなるかを見てみましょう。
_ public ngOnInit() {
const p = new Promise((resolver, reject) => {
setTimeout(() => reject(), 1000);
});
}
_
次のスタックトレースを取得します。
_core.js:6014 ERROR Error: Uncaught (in promise): [object Undefined]
at resolvePromise (zone-evergreen.js:797) [angular]
at :4200/polyfills.js:3942:17 [angular]
at :4200/app-module.js:21450:30 [angular]
at Object.onInvokeTask (core.js:39680) [angular]
at timer (zone-evergreen.js:2650) [<root>]
_
繰り返しになりますが、ここではコンテキストが失われており、バグを修正するための場所がわかりません。
オブザーバブルはエラー処理と同じ副作用を受けますが、一般にエラーメッセージはより良い品質です。誰かがthrowError(new Error())
を使用する場合、Errorオブジェクトにはスタックトレースが含まれ、HttpModule
を使用している場合、Errorオブジェクトは通常は、リクエストについて通知するHttp responseオブジェクトです。
つまり、ここでの話の教訓:エラーをキャッチし、async ngOnInit()
を使用できる場合はオブザーバブルを使用します。これは、見つけて修正するのが難しいバグとして悩まされるためです。
Rxjs関数of
を使用できます。
of(this.service.getData());
Promiseを監視可能なシーケンスに変換します。
GetData()がオブザーバブルを返す場合、それをpromiseに変更し、take(1)を使用してpromiseを解決できます。
import { take } from 'rxjs/operators';
async ngOnInit(): Promise<any> {
const data = await this.service.getData().pipe(take(1)).toPromise();
this.data = this.modifyMyData(data);
}