如果满足特定条件,RXJS 停止 Observable 链的传播
RXJS Stop propagation of Observable chain if certain condition is met
简介
我正在尝试使用共享服务中的 Observable 在 Angular2+
中创建路由守卫,该共享服务保存当前用户角色的字符串值。
问题显然在于将我的想法从 Promises 转移到 Observables。
What I made so far is based on heuristics and try & error approach but
I hit the wall by killing the browser Solved thanks to danday74
.
尝试(感谢 @danday74 改进)
在 的帮助下,我已经将我想做的事情翻译成这个链:
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | boolean {
return this.auth.isRoleAuthenticated(route.data.roles)
.mergeMap((isRoleAuthenticated: boolean) => {
return isRoleAuthenticated ? Observable.of(true) : this.auth.isRole(Roles.DEFAULT_USER);
})
.do((isDefaultUser: boolean) => {
const redirectUrl: string = isDefaultUser ? 'SOMEWHERE' : 'SOMEWHERE_ELSE';
this.router.navigate([redirectUrl]);
})
.map((isDefaultUser: boolean) => {
return false;
});
}
问题
如果isRoleAuthenticated = true
如何停止可观察链的进一步传播?如果满足这样的条件,我需要 return 那个布尔值,并确保 .do
运算符块不会在之后被调用。
限制是布尔值必须 return 来自 canActivate
guard.
第一个 mergeMap 中的任何 return 都会传递给第二个 mergeMap,因此它不会停止进一步传播。如果您想停止传播,请使用过滤器(尽管在这种情况下可能会导致挂起)。
只有在 return 一个 Observable 时才使用 mergeMap,但不需要 return 来自第二个 mergeMap 的 Observable。只要做:
.mergeMap((isRoleAuthenticated: boolean) => {
if (isRoleAuthenticated) {
return Observable.of(true)
}
return this.auth.isRole(Roles.DEFAULT_USER)
})
.tap((isDefaultUser: boolean) => {
if (isDefaultUser) {
this.router.navigate(['SOMEWHERE'])
} else {
this.router.navigate(['SOMEWHERE_ELSE'])
}
})
.map((isDefaultUser: boolean) => {
return false
})
此外,您使用的是 RxJs v5 语法,但应该使用 v6。在 v6 中,运算符 - mergeMap、tap、map - 在管道中以逗号分隔。
可能是路由器导航阻止了最终 return 并导致挂起?评论出来,看看它是否停止挂起。
不确定这是否会完全解决您的问题,但希望能为凌晨 1 点提供一些有用的见解
我假设这些 return Observables:
- this.auth.isRoleAuthenticated
- this.auth.isRole(Roles.DEFAULT_USER)
如果他们不这样做,你就会遇到问题。
解决方案
与其专注于停止链,不如创建一个由沿途收集的 Observable 结果组成的对象,并进一步传播它,从而解决问题:
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | boolean {
return this.auth.getRole()
.mergeMap((role: string) => {
return this.auth.isRoleAuthorized(route.data.roles)
.map((authorized: boolean) => ({
role: role,
authorized: authorized
}));
})
.do((markers: { role: string, authorized: boolean }) => {
const redirectUrl: string = markers.role === Roles.DEFAULT_USER ? 'SOMEWHERE' : 'SOMEWHERE_ELSE';
this.router.navigate([redirectUrl]);
})
.map((markers: { role: string, authorized: boolean }) => {
return markers.authorized;
});
}
do()
运算符仅适用于 next
通知,因此如果您不想处理 .mergeMap
之后的内容,您可以使用 filter()
将其过滤掉:
return this.auth.isRoleAuthenticated(route.data.roles)
.mergeMap((isRoleAuthenticated: boolean) => {
return isRoleAuthenticated ? Observable.of(true) : this.auth.isRole(Roles.DEFAULT_USER);
})
.filter(authenticated => authenticated !== true)
但是,看起来您可以 return Observable.empty()
而不是 Observable.of(true)
,因为那样只会发出 complete
通知而不会发出 next
项所以没有什么可以传递给 do()
:
.mergeMap((isRoleAuthenticated: boolean) => {
return isRoleAuthenticated ? Observable.empty() : this.auth.isRole(Roles.DEFAULT_USER);
})
您可以 return Observable.empty()
而不是 returning Observable.of(true)
,这将完成,不会发出任何值。因此以下链将不会被执行。
简介
我正在尝试使用共享服务中的 Observable 在 Angular2+
中创建路由守卫,该共享服务保存当前用户角色的字符串值。
问题显然在于将我的想法从 Promises 转移到 Observables。
What I made so far is based on heuristics and try & error approach but I hit the wall by killing the browser Solved thanks to danday74
.
尝试(感谢 @danday74 改进)
在
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | boolean {
return this.auth.isRoleAuthenticated(route.data.roles)
.mergeMap((isRoleAuthenticated: boolean) => {
return isRoleAuthenticated ? Observable.of(true) : this.auth.isRole(Roles.DEFAULT_USER);
})
.do((isDefaultUser: boolean) => {
const redirectUrl: string = isDefaultUser ? 'SOMEWHERE' : 'SOMEWHERE_ELSE';
this.router.navigate([redirectUrl]);
})
.map((isDefaultUser: boolean) => {
return false;
});
}
问题
如果isRoleAuthenticated = true
如何停止可观察链的进一步传播?如果满足这样的条件,我需要 return 那个布尔值,并确保 .do
运算符块不会在之后被调用。
限制是布尔值必须 return 来自 canActivate
guard.
第一个 mergeMap 中的任何 return 都会传递给第二个 mergeMap,因此它不会停止进一步传播。如果您想停止传播,请使用过滤器(尽管在这种情况下可能会导致挂起)。
只有在 return 一个 Observable 时才使用 mergeMap,但不需要 return 来自第二个 mergeMap 的 Observable。只要做:
.mergeMap((isRoleAuthenticated: boolean) => {
if (isRoleAuthenticated) {
return Observable.of(true)
}
return this.auth.isRole(Roles.DEFAULT_USER)
})
.tap((isDefaultUser: boolean) => {
if (isDefaultUser) {
this.router.navigate(['SOMEWHERE'])
} else {
this.router.navigate(['SOMEWHERE_ELSE'])
}
})
.map((isDefaultUser: boolean) => {
return false
})
此外,您使用的是 RxJs v5 语法,但应该使用 v6。在 v6 中,运算符 - mergeMap、tap、map - 在管道中以逗号分隔。
可能是路由器导航阻止了最终 return 并导致挂起?评论出来,看看它是否停止挂起。
不确定这是否会完全解决您的问题,但希望能为凌晨 1 点提供一些有用的见解
我假设这些 return Observables:
- this.auth.isRoleAuthenticated
- this.auth.isRole(Roles.DEFAULT_USER)
如果他们不这样做,你就会遇到问题。
解决方案
与其专注于停止链,不如创建一个由沿途收集的 Observable 结果组成的对象,并进一步传播它,从而解决问题:
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | boolean {
return this.auth.getRole()
.mergeMap((role: string) => {
return this.auth.isRoleAuthorized(route.data.roles)
.map((authorized: boolean) => ({
role: role,
authorized: authorized
}));
})
.do((markers: { role: string, authorized: boolean }) => {
const redirectUrl: string = markers.role === Roles.DEFAULT_USER ? 'SOMEWHERE' : 'SOMEWHERE_ELSE';
this.router.navigate([redirectUrl]);
})
.map((markers: { role: string, authorized: boolean }) => {
return markers.authorized;
});
}
do()
运算符仅适用于 next
通知,因此如果您不想处理 .mergeMap
之后的内容,您可以使用 filter()
将其过滤掉:
return this.auth.isRoleAuthenticated(route.data.roles)
.mergeMap((isRoleAuthenticated: boolean) => {
return isRoleAuthenticated ? Observable.of(true) : this.auth.isRole(Roles.DEFAULT_USER);
})
.filter(authenticated => authenticated !== true)
但是,看起来您可以 return Observable.empty()
而不是 Observable.of(true)
,因为那样只会发出 complete
通知而不会发出 next
项所以没有什么可以传递给 do()
:
.mergeMap((isRoleAuthenticated: boolean) => {
return isRoleAuthenticated ? Observable.empty() : this.auth.isRole(Roles.DEFAULT_USER);
})
您可以 return Observable.empty()
而不是 returning Observable.of(true)
,这将完成,不会发出任何值。因此以下链将不会被执行。