NgOnInit() 在构造函数之后/并行运行
Posted
技术标签:
【中文标题】NgOnInit() 在构造函数之后/并行运行【英文标题】:NgOnInit() Runs after/in parallel with constructor 【发布时间】:2021-11-27 09:32:23 【问题描述】: constructor(private firestore: AngularFirestore, private activatedRoute: ActivatedRoute)
this.firestore.collection('settings').doc('settings').valueChanges().subscribe(settings =>
console.log('getting settings');
this.settings = settings;
);
ngOnInit()
this.activatedRoute.queryParams.subscribe(params =>
console.log(this.settings);
然而,我的控制台输出是
未定义
获取设置
我怎样才能让一个接一个地跑?没有把所有东西都放在 NgOnInit 中,整个代码在 .then()
之后编辑: 我已将代码编辑为以下建议的答案
使用 forkJoin:
ngOnInit()
forkJoin([this.firestore.collection('settings').doc('settings').valueChanges(),
this.activatedRoute.queryParams]).subscribe(values =>
this.settings = values[0];
console.log('I am a console');
console.log(this.settings);
this.queryParams = values[1];
if (this.queryParams['myParam'])
this.setmyParamTrue();
);
但是,无论是否有任何 queryParams,都不会进入控制台
不用说,我只需要从 firestore 中检索设置一次,而且参数不必存在。
【问题讨论】:
构造函数总是首先触发(所以在 ngOnInit 之前) - 但是,您在构造函数中执行异步操作,并且您的控制台位于回调内部,因此稍后触发。一个提示——不要在构造函数中做实际的工作——在 ngOnInit 中做。要解决此问题,您需要使用 RxJs 运算符 - 就像在这种情况下,您可以 switchMap 或使用 combineLatest 一次订阅两个可观察对象。 我已经编辑了我的帖子。它实际上是在构造函数中来说明我的观点 【参考方案1】:情况
来自您的原始代码:
1 class SomeComponent
2
3 constructor(private firestore: AngularFirestore, private route: ActivatedRoute)
4 this.firestore.collection().doc().valueChanges().subscribe(settings =>
5 console.log('getting settings');
6 this.settings = settings;
7 );
8
9
10 ngOnInit()
11 this.route.queryParams.subscribe(params =>
12 console.log(this.settings);
13
14
15
在第 4 行的 constructor
中,您只是创建了一个 Subscription
。您传递给.subscribe()
方法的逻辑将在每次可观察对象发出一个值时执行。
在第 11 行的 ngOnInit
中,您正在创建另一个订阅。
回调函数的执行顺序完全取决于每个 observable 的发射时间,而不是您注册订阅的时间! (显然在您的情况下,来自ActivatedRoute
的可观察对象首先发出,然后是来自AngularFireStore
的观察对象。)
NgOnInit() 在构造函数之后/并行运行
希望你能明白为什么这个说法不正确!
目标
我怎样才能一个接一个地跑?
您正试图在您的回调中访问this.settings
,但它还没有准备好!解决方案是创建一个仅在 settings
值准备好后才开始的 observable。
让我们将您的设置定义为可观察对象:
1 class SomeComponent
2
3 private settings$ = this.firestore.collection().doc().valueChanges();
4
5 constructor(private firestore: AngularFirestore, private route: ActivatedRoute)
6
7 ngOnInit()
8 this.settings$.pipe(
9 switchMap(settings => this.route.queryParams.pipe(
10 tap(params => console.log('both values', settings, params ))
11 ))
12 ).subscribe();
13
14
您在第 8 行看到,我们从this.setting$
和pipe
开始,将其排放到switchMap
,这将订阅内部源并排放源的排放。在这种情况下,您的内部来源是可观察的参数。
我们将pipe
放在可观察的内部参数上,以便tap
可以访问settings
和发出的params
。
tap
对执行副作用很有用,通常比将逻辑放入订阅回调中更受欢迎。
根据您在ngOnInit
中订阅此订阅的目的,单独定义可观察对象可能会有所帮助,就像我们对settings$
所做的那样:
1 class SomeComponent
2
3 private settings$ = this.firestore.collection().doc().valueChanges();
4 private doWork$ = this.settings$.pipe(
5 switchMap(settings => this.route.queryParams.pipe(
6 tap(params => console.log('both values', settings, params ))
7 ))
8 );
9
10 constructor(private firestore: AngularFirestore, private route: ActivatedRoute)
11
12 ngOnInit()
13 this.doWork$.subscribe();
14
15
【讨论】:
感谢您的详尽回复。但是,您的第二个代码块出现了很多错误:来自 tslint:TS2345:“ObservableswitchMap
已弃用,它不是:-)
使用此代码,queryParams
不依赖于settings$
; doWork$
依赖于先获取设置,然后使用queryParams
。如果在代码的其他地方使用了设置,您也可以简单地声明其他可观察对象也以settings$
开头。 (doWork2$ = settings$.pipe(...)
)【参考方案2】:
有两个运算符可以帮助您将 2 个 observables 相互使用:
-
forkJoin
结合最新
切换地图
如果你需要同时运行它,请使用forkJoin
,如下所示:
constructor(
private firestore: AngularFirestore,
private activatedRoute: ActivatedRoute)
forkJoin([
this.firestore.collection('settings').doc('settings').valueChanges(),
this.activatedRoute.queryParams
]).subscribe(values =>
this.settings = values[0]; //<- you'll get settings here
console.log(values[1]);
// use the call accordingly.
)
同样,如果您需要连续收听,可以使用combineLatest
。并使用switchMap
。完全取决于用例。
【讨论】:
我已经编辑了我的帖子以上是关于NgOnInit() 在构造函数之后/并行运行的主要内容,如果未能解决你的问题,请参考以下文章