【问题标题】:RXJS Stop propagation of Observable chain if certain condition is met如果满足某些条件,RXJS 停止 Observable 链的传播
【发布时间】:2019-03-21 13:28:07
【问题描述】:

简介

我正在尝试使用共享服务中的 Observable 在 Angular2+ 中创建路由保护,该共享服务包含当前用户角色的字符串值。
问题显然在于将我的想法从 Promise 转移到 Observables。

到目前为止,我所做的是基于启发式和尝试错误的方法,但是 我通过杀死浏览器撞墙感谢danday74解决

.

尝试(感谢@danday74 改进)

RxJS sequence equvalent to promise.then()? 的帮助下,我将我想做的事情翻译成这个链:

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,如何阻止可观察链的进一步传播?如果满足此类条件,我需要返回该布尔值,并确保之后不调用 .do 运算符块。
限制是必须从canActivateguard 返回布尔值。

【问题讨论】:

  • 浏览器实际上只是永远加载而不是崩溃,抱歉表达不好。尝试使用setTimeout()take(1),我仍然无法调试并且浏览器永远加载,直到页面变得无响应。

标签: angular rxjs observable angular-observable angular-router-guards


【解决方案1】:

无论您从第一个 mergeMap 返回什么都将传递给您的第二个 mergeMap,因此它不会停止进一步的传播。如果您想停止传播,请使用过滤器(尽管在这种情况下可能会导致挂起)。

只有在返回 Observable 时才使用 mergeMap,但不需要从第二个 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 - 在管道中以逗号分隔。

可能是路由器导航阻止了最终返回并导致挂起?将其注释掉,看看它是否会停止挂起。

不确定这是否会完全解决您的问题,但希望对凌晨 1 点有一些有用的见解

我假设这些返回 Observables:

  • this.auth.isRoleAuthenticated
  • this.auth.isRole(Roles.DEFAULT_USER)

如果他们不这样做,你就会遇到问题。

解决方案

您可以创建一个由沿途收集的 Observables 结果组成的对象,而不是专注于停止链,并进一步传播它,从而解决问题:

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;
        });
}

【讨论】:

  • 感谢目前无法调试,希望您的见解至少能让我更接近恢复chrome 我正在使用RxJs V5Angular 5。是的,auth 方法正在正确返回 Observables,我将更新问题并提供更多详细信息,以便尝试根据您的建议进行更改。是的,我已经发现我不需要第二个 mergeMap,因为那里没有内部 Observables。
  • 值得了解,在链的每个阶段使用 ....tap(x => console.log(x)) 以便您可以看到正在传递的值 - 有利于调试 -一切顺利:)
  • 另外值得了解的是,在 Chrome 中,确保选中“保留日志”复选框,这样它就不会清除导航日志,否则您将丢失所有有用的调试信息 - 抱歉,如果我'我教你吸鸡蛋
【解决方案2】:

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)

但是,您似乎可以只返回Observable.empty() 而不是Observable.of(true),因为这只会发出complete 通知而没有next 项,因此不会有任何东西可以传递给do()

.mergeMap((isRoleAuthenticated: boolean) => {
  return isRoleAuthenticated ? Observable.empty() : this.auth.isRole(Roles.DEFAULT_USER);
})

【讨论】:

  • 谢谢@martin,我曾考虑过使用Observable.empty(),但问题是我必须将boolean true 值返回给canActivate 守卫,作为该特定情况下链的输出。如果不进一步传播任何东西,我似乎无法做到
  • 我明白了。好吧,那么Observable.empty() 实在不行。
  • ATM 我正在​​尝试传播一个新的合并对象,然后在进一步的步骤中进行预测。我会在我完成后发布,然后我们可以改进它。
【解决方案3】:

您可以返回Observable.empty(),而不是返回Observable.of(true),这将刚刚完成,不会发出任何值。因此不会执行以下链。

【讨论】:

  • 不幸的是,在这种特定情况下我需要返回boolean true,因为canActivate 需要它,否则这将是解决方案
猜你喜欢
  • 2020-01-11
  • 2020-07-16
  • 2011-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多