qqi*_*ihq 12 asynchronous promise observable typescript angular
我目前正在评估替换Angular的优点的利弊。RxJS” Observable
与普通的Promise
,这样我可以使用async
和await
并获得更直观的代码风格。
我们的典型场景之一:在中加载一些数据ngOnInit
。使用Observables
,我们可以:
ngOnInit () {
this.service.getData().subscribe(data => {
this.data = this.modifyMyData(data);
});
}
Run Code Online (Sandbox Code Playgroud)
当我改为返回Promise
from getData()
,并使用async
和时await
,它变为:
async ngOnInit () {
const data = await this.service.getData();
this.data = this.modifyMyData(data);
}
Run Code Online (Sandbox Code Playgroud)
现在,很明显,Angular不会“知道”,而ngOnInit
变成了async
。我觉得这不是问题:我的应用仍然可以像以前一样工作。但是,当我查看OnInit
接口时,显然并未以暗示可以声明该函数的方式声明该函数async
:
ngOnInit(): void;
Run Code Online (Sandbox Code Playgroud)
所以-底线:我在这里做什么合理吗?还是会遇到任何无法预料的问题?
Rea*_*lar 27
现在,很明显,Angular 不会“知道”ngOnInit 已经变成异步了。我觉得这不是问题:我的应用程序仍然像以前一样工作。
从语义上讲,它可以正常编译并按预期运行,但是编写的便利async / wait
是以错误处理为代价的,我认为应该避免这种情况。
让我们看看会发生什么。
当 promise 被拒绝时会发生什么:
public ngOnInit() {
const p = new Promise((resolver, reject) => reject(-1));
}
Run Code Online (Sandbox Code Playgroud)
以上生成了以下堆栈跟踪:
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]
.....
Run Code Online (Sandbox Code Playgroud)
我们可以清楚地看到未处理的错误是由 a 触发的ngOnInit
,还可以看到哪个源代码文件可以找到有问题的代码行。
当我们使用async/wait
拒绝时会发生什么:
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]
.....
Run Code Online (Sandbox Code Playgroud)
以上生成了以下堆栈跟踪:
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>]
Run Code Online (Sandbox Code Playgroud)
发生了什么?我们不知道,因为堆栈跟踪在组件之外。
不过,您可能会倾向于使用 Promise 而只是避免使用async / wait
. 所以让我们看看如果一个 promise 在 a 之后被拒绝会发生什么setTimeout()
。
public async ngOnInit() {
const p = await new Promise((resolver, reject) => reject());
}
Run Code Online (Sandbox Code Playgroud)
我们将得到以下堆栈跟踪:
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>]
Run Code Online (Sandbox Code Playgroud)
同样,我们在这里失去了上下文,不知道去哪里修复错误。
Observable 遭受与错误处理相同的副作用,但通常错误消息的质量更好。如果有人使用throwError(new Error())
了错误的对象将包含一个堆栈跟踪,如果你正在使用HttpModule
的错误的对象通常是一个HTTP响应,告诉你有关请求的对象。
所以这里的故事的寓意是:抓住你的错误,在你可以和不使用的情况下使用 observables async ngOnInit()
,因为它会作为一个难以找到和修复的错误再次困扰你。
Pac*_*ace 13
这与您之前的没什么不同。 ngOnInit
将返回一个Promise,而调用方将忽略该Promise。这意味着调用方不会继续进行方法中的所有操作。在这种特定情况下,这意味着该视图将完成配置,并且该视图可在this.data
设置之前启动。
这与您以前的情况相同。呼叫者不会等待您的订阅完成,因此可能会this.data
在填充之前启动应用程序。如果您的视图依赖于此,data
则可能需要进行某种ngIf
设置以防止您访问它。
只要您知道其中的含义,我个人就不会将其视为尴尬或不当行为。但是,这ngIf
可能很乏味(无论哪种方式都需要)。我个人已经转向使用有意义的路由解析器,因此可以避免这种情况。在路线完成导航之前就已经加载了数据,而且我知道在加载视图之前就可以使用该数据。
小智 6
我想知道立即调用函数表达式的缺点是什么:
ngOnInit () {
(async () => {
const data = await this.service.getData();
this.data = this.modifyMyData(data);
})();
}
Run Code Online (Sandbox Code Playgroud)
这是我能想象到的唯一方法可以让它在不声明ngOnInit()
为async
函数的情况下工作
ngOnInit
不等待承诺完成。如果您想像这样使用await,您可以将其设为异步函数:
import { take } from 'rxjs/operators';
async ngOnInit(): Promise<any> {
const data = await firstValueFrom(this.service.getData());
this.data = this.modifyMyData(data);
}
Run Code Online (Sandbox Code Playgroud)
但是,如果您使用ngOnInit
构造函数而不是等待函数完成,则基本上相当于执行以下操作:
import { take } from 'rxjs/operators';
constructor() {
firstValueFrom(this.service.getData())
.then((data => {;
this.data = this.modifyMyData(data);
});
}
Run Code Online (Sandbox Code Playgroud)
它将运行异步函数,但不会等待它完成。如果您注意到有时它会完成,有时则不会,那么这实际上仅取决于您的函数的时间安排。
使用这篇文章中的想法,您基本上可以跑到外面zone.js
。NgZone
不包括scheduleMacroTask
,但zone.js
已导入到 Angular 中。
import { isObservable, Observable } from 'rxjs';
import { take } from 'rxjs/operators';
declare const Zone: any;
async waitFor<T>(prom: Promise<T> | Observable<T>): Promise<T> {
if (isObservable(prom)) {
prom = firstValueFrom(prom);
}
const macroTask = Zone.current
.scheduleMacroTask(
`WAITFOR-${Math.random()}`,
() => { },
{},
() => { }
);
return prom.then((p: T) => {
macroTask.invoke();
return p;
});
}
Run Code Online (Sandbox Code Playgroud)
我个人把这个函数放在 my 中core.module.ts
,尽管你可以把它放在任何地方。
像这样使用它:
constructor(private cm: CoreModule) {
const p = this.service.getData();
this.post = this.cm.waitFor(p);
}
Run Code Online (Sandbox Code Playgroud)
您还可以检查isBrowser以保持可观察性,或等待结果。
相反,您也可以像本文angular-zen
中一样导入和使用它,尽管您将导入比您需要的更多的内容。
J
更新:2/26/22 - 现在使用firstValueFrom
,因为.toPromise()
已折旧。
我在 ngOnInit() 中使用了 try catch:
async ngOnInit() {
try {
const user = await userService.getUser();
} catch (error) {
console.error(error);
}
}
Run Code Online (Sandbox Code Playgroud)
然后你会得到一个更具描述性的错误,你可以找到错误所在
归档时间: |
|
查看次数: |
2895 次 |
最近记录: |