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
async/await in Angular `ngOnInit`
提问by qqilihq
I'm currently evaluating the pros ‘n' cons of replacing Angular's resp. RxJS' Observable
with plain Promise
so that I can use async
and await
and get a more intuitive code style.
我目前正在评估替换 Angular 的优缺点。RxJS”Observable
与普通的Promise
,这样我可以使用async
和await
并获得更直观的代码风格。
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 Promise
from getData()
instead, and use async
and await
, it becomes:
当我返回 a Promise
fromgetData()
并使用async
and 时await
,它变成:
async ngOnInit () {
const data = await this.service.getData();
this.data = this.modifyMyData(data);
}
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. But when I look at the OnInit
interface, 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. ngOnInit
will 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.data
is 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.data
had been populated. If your view is relying on data
then you likely have some kind of ngIf
setup 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 ngIf
can 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 / wait
comes 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 ngOnInit
and also see which source code file to find the offending line of code.
我们可以清楚地看到未处理的错误是由 a 触发的ngOnInit
,还可以看到哪个源代码文件可以找到有问题的代码行。
What happens when we use async/wait
that 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 HttpModule
the 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);
}