typescript Angular `ngOnInit` 中的异步/等待

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/56092083/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 05:49:36  来源:igfitidea点击:

async/await in Angular `ngOnInit`

angulartypescriptasynchronouspromiseobservable

提问by qqilihq

I'm currently evaluating the pros ‘n' cons of replacing Angular's resp. RxJS' Observablewith plain Promiseso that I can use asyncand awaitand get a more intuitive code style.

我目前正在评估替换 Angular 的优缺点。RxJS”Observable与普通的Promise,这样我可以使用asyncawait并获得更直观的代码风格。

One of our typical scenarios: Load some data within ngOnInit. Using Observables, we do:

我们的典型场景之一:在ngOnInit. 使用Observables,我们这样做:

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

When I return a Promisefrom getData()instead, and use asyncand await, it becomes:

当我返回 a PromisefromgetData()并使用asyncand 时await,它变成:

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

Now, obviously, Angular will not “know”, that ngOnInithas become async. I feel that this is not a problem: My app still works as before. But when I look at the OnInitinterface, the function is obviously not declared in such a way which would suggest that it can be declared async:

现在,很明显,Angular 不会“知道”,那ngOnInit已经变成了async. 我觉得这不是问题:我的应用程序仍然像以前一样工作。但是当我查看OnInit接口时,该函数显然没有以表明可以声明的方式声明async

ngOnInit(): void;

So -- bottom line: Is it reasonable what I'm doing here? Or will I run into any unforseen problems?

所以——底线:我在这里做的事情合理吗?或者我会遇到任何不可预见的问题?

采纳答案by Pace

It is no different than what you had before. ngOnInitwill return a Promise and the caller will ignore that promise. This means that the caller will not wait for everything in your method to finish before it proceeds. In this specific case it means the view will finish being configured and the view may be launched before this.datais set.

它和你以前的没有什么不同。 ngOnInit将返回一个承诺,调用者将忽略该承诺。这意味着调用者在继续之前不会等待方法中的所有内容完成。在这种特定情况下,这意味着视图将完成配置,并且视图可能会在this.data设置之前启动。

That is the same situation you had before. The caller would not wait for your subscriptions to finish and would possibly launch the app before this.datahad been populated. If your view is relying on datathen you likely have some kind of ngIfsetup to prevent you from accessing it.

这和你之前的情况一样。调用者不会等待您的订阅完成,并且可能会this.data在填充之前启动应用程序。如果您的视图依赖,data那么您可能有某种ngIf设置来阻止您访问它。

I personally don't see it as awkward or a bad practice as long as you're aware of the implications. However, the ngIfcan be tedious (they would be needed in either way). I have personally moved to using route resolvers where it makes sense so I can avoid this situation. The data is loaded before the route finishes navigating and I can know the data is available before the view is ever loaded.

我个人并不认为这是尴尬或不好的做法,只要你意识到其中的含义。但是,这ngIf可能很乏味(无论哪种方式都需要它们)。我个人已经开始在有意义的地方使用路由解析器,这样我就可以避免这种情况。在路线完成导航之前加载数据,我可以在加载视图之前知道数据可用。

回答by Reactgular

Now, obviously, Angular will not “know”, that ngOnInit has become async. I feel that this is not a problem: My app still works as before.

现在,很明显,Angular 不会“知道”ngOnInit 已经变成异步了。我觉得这不是问题:我的应用程序仍然像以前一样工作。

Semantically it will compile fine and run as expected, but the convenience of writing async / waitcomes at a cost of error handling, and I think it should be avoid.

从语义上讲,它可以正常编译并按预期运行,但是编写的便利async / wait是以错误处理为代价的,我认为应该避免这种情况。

Let's look at what happens.

让我们看看会发生什么。

What happens when a promise is rejected:

当承诺被拒绝时会发生什么:

public ngOnInit() {
    const p = new Promise((resolver, reject) => reject(-1));
}

The above generates the following stack trace:

以上生成了以下堆栈跟踪:

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

We can clearly see that the unhandled error was triggered by a ngOnInitand also see which source code file to find the offending line of code.

我们可以清楚地看到未处理的错误是由 a 触发的ngOnInit,还可以看到哪个源代码文件可以找到有问题的代码行。

What happens when we use async/waitthat is reject:

当我们使用async/wait拒绝时会发生什么:

    public async ngOnInit() {
        const p = await new Promise((resolver, reject) => reject());
    }

The above generates the following stack trace:

以上生成了以下堆栈跟踪:

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

What happened? We have no clue, because the stack trace is outside of the component.

发生了什么?我们不知道,因为堆栈跟踪在组件之外。

Still, you might be tempted to use promises and just avoid using async / wait. So let's see what happens if a promise is rejected after a setTimeout().

不过,您可能会倾向于使用 Promise 而只是避免使用async / wait. 因此,让我们看看如果在setTimeout().

    public ngOnInit() {
        const p = new Promise((resolver, reject) => {
            setTimeout(() => reject(), 1000);
        });
    }

We will get the following stack trace:

我们将得到以下堆栈跟踪:

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

Again, we've lost context here and don't know where to go to fix the bug.

同样,我们在这里失去了上下文,不知道去哪里修复错误。

Observables suffer from the same side effects of error handling, but generallythe error messages are of betterquality. If someone uses throwError(new Error())the Errorobject will contain a stack trace, and if you're using the HttpModulethe Errorobject is usually a Http responseobject that tells you about the request.

Observables 遭受与错误处理相同的副作用,但通常错误消息的质量更好。如果有人使用throwError(new Error())错误的对象将包含一个堆栈跟踪,如果你正在使用HttpModule错误的对象通常是一个HTTP响应,告诉你有关请求的对象。

So the moral of the story here: Catch your errors, use observables when you can and don't use async ngOnInit(), because it will come back to haunt you as a difficult bug to find and fix.

所以这个故事的寓意是:抓住你的错误,在你可以和不使用的时候使用 observables async ngOnInit(),因为它会作为一个难以找到和修复的错误再次困扰你。

回答by ocknamo-bb

You can use rxjs function of.

您可以使用 rxjs 函数of

of(this.service.getData());

Converts the promise to an observable sequence.

将承诺转换为可观察序列。

回答by Jonathan

If getData() returns an observable, you could change it to a promise and resolve the promise with take(1).

如果 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);
}