簡體   English   中英

Angular`ngOnInit`中的異步/等待

[英]async/await in Angular `ngOnInit`

我目前正在評估替換 Angular 的利弊。 RxJS 的Observable帶有普通的Promise ,這樣我就可以使用asyncawait並獲得更直觀的代碼風格。

我們的典型場景之一:在ngOnInit中加載一些數據。 使用Observables ,我們可以:

ngOnInit () {
  this.service.getData().subscribe(data => {
    this.data = this.modifyMyData(data);
  });
}

當我從getData()返回Promise並使用asyncawait時,它變為:

async ngOnInit () {
  const data = await this.service.getData();
  this.data = this.modifyMyData(data);
}

現在,很明顯,Angular 不會“知道” ngOnInit已經變成了async 我覺得這不是問題:我的應用程序仍然像以前一樣工作。 但是當我查看OnInit接口時,該函數顯然沒有以表明它可以聲明為async的方式聲明:

ngOnInit(): void;

所以——底線:我在這里做的事情合理嗎? 還是我會遇到任何不可預見的問題?

它和你以前的沒有什么不同。 ngOnInit將返回一個 Promise 並且調用者將忽略該承諾。 這意味着調用者在繼續之前不會等待方法中的所有內容完成。 在這種特定情況下,這意味着視圖將完成配置,並且視圖可能會在設置this.data之前啟動。

這和你之前遇到的情況一樣。 調用者不會等待您的訂閱完成,並且可能會在填充this.data之前啟動應用程序。 如果您的視圖依賴於data那么您可能有某種ngIf設置來阻止您訪問它。

我個人並不認為這是尷尬或不好的做法,只要你意識到其中的含義。 但是, ngIf可能很乏味(無論哪種方式都需要它們)。 我個人已經轉向在有意義的地方使用路由解析器,這樣我就可以避免這種情況。 在路線完成導航之前加載數據,我可以在加載視圖之前知道數據可用。

現在,很明顯,Angular 不會“知道”ngOnInit 已經變成異步了。 我覺得這不是問題:我的應用程序仍然像以前一樣工作。

從語義上講,它可以正常編譯並按預期運行,但是編寫async / wait的便利是以錯誤處理為代價的,我認為應該避免這種情況。

讓我們看看會發生什么。

當 promise 被拒絕時會發生什么:

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>]

同樣,我們在這里失去了上下文,不知道去哪里修復錯誤。

Observable 遭受與錯誤處理相同的副作用,但通常錯誤消息的質量更好 如果有人使用throwError(new Error())Error對象將包含堆棧跟蹤,如果您使用的是HttpModuleError對象通常是一個Http 響應對象,它會告訴您有關請求的信息。

所以這里的故事的寓意是:捕捉你的錯誤,在你可以的時候使用 observables 並且不要使用async ngOnInit() ,因為它會作為一個難以找到和修復的錯誤再次困擾你。

我想知道立即調用函數表達式的缺點是什么:

    ngOnInit () {
      (async () => {
        const data = await this.service.getData();
        this.data = this.modifyMyData(data);
      })();
    }

這是我能想象到的唯一方法是讓它在不將ngOnInit()聲明為async函數的情況下工作

您可以使用rxjs功能of

of(this.service.getData());

將承諾轉換為可觀察序列。

我在 ngOnInit() 中使用了 try catch:

async ngOnInit() {      
   try {           
       const user = await userService.getUser();
    } catch (error) {           
        console.error(error);       
    }    
} 

然后你會得到一個更具描述性的錯誤,你可以找到錯誤在哪里

如果 getData() 返回一個 observable,您可以將其更改為承諾並使用 take(1) 解決該承諾。

import { take } from 'rxjs/operators';

async ngOnInit(): Promise<any> {
  const data = await this.service.getData().pipe(take(1)).toPromise();
  this.data = this.modifyMyData(data);
}

我會做一些不同的事情,你不顯示你的 html 模板,但我假設你正在用數據做一些事情,即

<p> {{ data.Name }}</p> <!-- or whatever -->

您是否有理由不使用異步管道?,即

<p> {{ (data$ | async).Name }}</p>

或者

<p *ngIf="(data$ | async) as data"> {{ data.name }} </p>

在你的ngOnInit

data$: Observable<any>; //change to your data structure
ngOnInit () {
  this.data$ = this.service.getData().pipe(
    map(data => this.modifyMyData(data))
  );
}

這個問題的正確答案是使用 Angular 的Resolver功能。 使用 Resolve,在您告訴它渲染之前,您的組件不會被渲染。

來自文檔:

類可以實現為數據提供者的接口。 數據提供者類可以與路由器一起使用,以在導航期間解析數據。 該接口定義了一個在導航開始時調用的 resolve() 方法。 路由器在路由最終激活之前等待數據解析。

說白了,你的所作所為,是不合理的。 如果您在導航到該頁面之前有哪些可用數據,請在您的路線中使用解析器

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM