嵌套的可观察对象,其中第一个可观察对象的响应传递给第二个,并且两个响应都用于最终订阅
Nested observables where response from first observable is passed to the second and both responses are used in the final subscription
不使用嵌套订阅的正确写法是什么?
第一个 observable 将 return 传递给第二个 observable 的值,然后我需要在最终订阅中使用来自两个 observable 的 returned 值。
此外,“doStuff”方法是通过单击按钮调用的,我是否还需要取消订阅这些可观察对象以防止内存泄漏?
doStuff() {
// Get app user
this.appUserService.getAppUser(true).subscribe((appUser) => {
// Get login key
this.accountApi.getLoginKey(appUser).subscribe(loginKey => {
// Open in app browser
this.browser = this.inAppBrowser.create(appUser.Url + loginKey, '_system');
});
});
}
编辑 1
按照建议尝试了以下操作,但无法编译,在第二个管道上出现以下错误:
属性 'pipe' 在类型 'OperatorFunction<AppUser, any>'
上不存在
然后在“地图”功能中出现以下错误:
找不到名称 'appUser'。
return this.appUserService.getAppUser(true).pipe(
switchMap((appUser: AppUser) => this.accountApi.getUserLoginKey(appUser)).pipe(
map(loginKey => { appUser, loginKey; }),
),
tap(({ appUser, loginKey }) => {
this.browser = this.inAppBrowser.create(appUser.Url + loginKey, '_system');
}),
take(1)
);
编辑 2
我现在已经更新了我的解决方案,如下所示,它有效(但我可以争辩说我原来的解决方案确实有效),所以我删除了嵌套订阅,但它们已被嵌套管道取代。我相信这是一个更可接受的解决方案(即使用嵌套的“管道”而不是嵌套的“订阅”)但不确定是否可以进一步改进?
doStuff() {
// Get app user
this.appUserService.getAppUser(true).pipe(
switchMap(appUser => this.accountApi.getLoginKey(appUser).pipe(
tap(loginKey => {
// Open in app browser as return URL
this.browser = this.inAppBrowser.create(appUser.Url + loginKey, '_system');
})
)),
take(1)
).subscribe();
}
没有“正确的方法”,你有的代码已经很好了。如果你想稍微改进一下,你可以使用 rxjs(例如 switchMap)。
关于退订部分,退订总是更好更安全。使用 Angular,您可以在 HTML 中使用异步管道,或者简单地在组件的销毁过程中取消订阅。
“正确”方式完全取决于您要查找的特定行为。如果您一次只希望一个请求处于活动状态,请使用 switchMap()
或 exhaustMap()
(新的点击将取消先前的请求或将分别被完全忽略)。这是一种方法:
doStuff() {
return this.appUserService.getAppUser(true).pipe(
switchMap(appUser => this.accountApi.getLoginKey(appUser).pipe(
map(loginKey => { appUser, loginKey }),
)),
tap(({ appUser: /* appUser type */, loginKey: /* loginKey type */ }) => {
this.browser = this.inAppBrowser.create(appUser.Url + loginKey, '_system');
}),
// OPTIONAL: add this take(1) to make sure Observable completes after first emission
// if that is the desired behaviour (one way to prevent leaks)
take(1)
);
}
更新
修改了代码以删除已弃用的 switchMap
和 resultSelector
。
第二次更新
更正括号。
这里有两个问题,
如何解决嵌套订阅情况? @Will Alexander 已经回答了这个问题。以下是一些可能对您有所帮助的参考资料
https://angular-checklist.io/default/checklist/rxjs/Z1eFwa9
https://www.thinktecture.com/en/angular/rxjs-antipattern-1-nested-subs/
如何优雅地取消订阅?
a) 您可以手动取消订阅您的每个订阅(这很快就会变得混乱)
b) 如果您不再需要 doStuff() 流,那么您可以按照说明使用 take(1)
c) Angular 建议尽可能使用异步管道,以便框架执行订阅和取消订阅部分
d) 使用 takeUntil 运算符取消订阅所有订阅(当您有许多订阅未使用异步管道管理时,这会派上用场)https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87
不使用嵌套订阅的正确写法是什么?
第一个 observable 将 return 传递给第二个 observable 的值,然后我需要在最终订阅中使用来自两个 observable 的 returned 值。
此外,“doStuff”方法是通过单击按钮调用的,我是否还需要取消订阅这些可观察对象以防止内存泄漏?
doStuff() {
// Get app user
this.appUserService.getAppUser(true).subscribe((appUser) => {
// Get login key
this.accountApi.getLoginKey(appUser).subscribe(loginKey => {
// Open in app browser
this.browser = this.inAppBrowser.create(appUser.Url + loginKey, '_system');
});
});
}
编辑 1
按照建议尝试了以下操作,但无法编译,在第二个管道上出现以下错误:
属性 'pipe' 在类型 'OperatorFunction<AppUser, any>'
上不存在然后在“地图”功能中出现以下错误:
找不到名称 'appUser'。
return this.appUserService.getAppUser(true).pipe(
switchMap((appUser: AppUser) => this.accountApi.getUserLoginKey(appUser)).pipe(
map(loginKey => { appUser, loginKey; }),
),
tap(({ appUser, loginKey }) => {
this.browser = this.inAppBrowser.create(appUser.Url + loginKey, '_system');
}),
take(1)
);
编辑 2
我现在已经更新了我的解决方案,如下所示,它有效(但我可以争辩说我原来的解决方案确实有效),所以我删除了嵌套订阅,但它们已被嵌套管道取代。我相信这是一个更可接受的解决方案(即使用嵌套的“管道”而不是嵌套的“订阅”)但不确定是否可以进一步改进?
doStuff() {
// Get app user
this.appUserService.getAppUser(true).pipe(
switchMap(appUser => this.accountApi.getLoginKey(appUser).pipe(
tap(loginKey => {
// Open in app browser as return URL
this.browser = this.inAppBrowser.create(appUser.Url + loginKey, '_system');
})
)),
take(1)
).subscribe();
}
没有“正确的方法”,你有的代码已经很好了。如果你想稍微改进一下,你可以使用 rxjs(例如 switchMap)。
关于退订部分,退订总是更好更安全。使用 Angular,您可以在 HTML 中使用异步管道,或者简单地在组件的销毁过程中取消订阅。
“正确”方式完全取决于您要查找的特定行为。如果您一次只希望一个请求处于活动状态,请使用 switchMap()
或 exhaustMap()
(新的点击将取消先前的请求或将分别被完全忽略)。这是一种方法:
doStuff() {
return this.appUserService.getAppUser(true).pipe(
switchMap(appUser => this.accountApi.getLoginKey(appUser).pipe(
map(loginKey => { appUser, loginKey }),
)),
tap(({ appUser: /* appUser type */, loginKey: /* loginKey type */ }) => {
this.browser = this.inAppBrowser.create(appUser.Url + loginKey, '_system');
}),
// OPTIONAL: add this take(1) to make sure Observable completes after first emission
// if that is the desired behaviour (one way to prevent leaks)
take(1)
);
}
更新
修改了代码以删除已弃用的 switchMap
和 resultSelector
。
第二次更新
更正括号。
这里有两个问题,
如何解决嵌套订阅情况? @Will Alexander 已经回答了这个问题。以下是一些可能对您有所帮助的参考资料
https://angular-checklist.io/default/checklist/rxjs/Z1eFwa9
https://www.thinktecture.com/en/angular/rxjs-antipattern-1-nested-subs/如何优雅地取消订阅?
a) 您可以手动取消订阅您的每个订阅(这很快就会变得混乱)
b) 如果您不再需要 doStuff() 流,那么您可以按照说明使用 take(1)
c) Angular 建议尽可能使用异步管道,以便框架执行订阅和取消订阅部分
d) 使用 takeUntil 运算符取消订阅所有订阅(当您有许多订阅未使用异步管道管理时,这会派上用场)https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87