【问题标题】:Transform to Observable only if not already Observable仅当不是 Observable 时才转换为 Observable
【发布时间】:2021-09-30 10:07:51
【问题描述】:

我有一张支票,需要合并两张支票:

  1. 第一个检查是观察用户是否登录
  2. 第二个检查是一个可以返回布尔值或 Observable 的函数

有没有一种更优雅的方式将一个值包装在一个可观察的值中,如果它已经不是可观察的吗? 或者当第二个是 Observable 时,有没有办法将第一个检查与第二个检查结合起来? 还是以更有效的方式重构所有这些?

当然,这段代码是模拟我在真实应用中得到的示例(参见代码中的 cmets)。

const getRandomBool: () => boolean = () => Math.random() >= 0.5;
const getRandomCheck: () => boolean | Observable<boolean> = () => {
  return getRandomBool() ? getAsyncCheck() : getSyncCheck();
};


const myFunction = (): Observable<boolean> | boolean => {

  // First check telling if user is logged, which the UserService returns as Observable<boolean>
  return of(getRandomBool())
    .pipe(
      mergeMap((isLogged) => {
        // This is the check which can be either boolean or Observable<boolean>
        const myCheck = getRandomCheck();
        
        return isLogged
            ? ​i​if(() => typeof myCheck === 'boolean', of(myCheck), myCheck)
            : of(!1)
     ​})
   ​)
};
``

【问题讨论】:

    标签: javascript rxjs refactoring


    【解决方案1】:

    首先检查“myCheck”是否为 Observable 并使用 forkJoin 怎么样?

    const myFunction: () => Observable<boolean> = () => {
      let myCheck = getRandomCheck();
      myCheck = isObservable(myCheck) ? myCheck : of(myCheck);
    
      return forkJoin([of(getRandomBool()), myCheck])
      .pipe(
        map(([logged, yourCheck]) => {
    
          return logged ? yourCheck : !1;
        })
      )
    }
    

    编辑 正如 BizzyBob 所建议的那样,如果 myCheck 是异步的并且跨度很大,它可能会毫无意义地影响整个执行。 所以我们可以使用switchMap 来避免这个问题

    const myFunction: () => Observable<boolean> = () => {
      let myCheck = getRandomCheck();
      myCheck = isObservable(myCheck) ? myCheck : of(myCheck);
    
      return of(getRandomBool())
      .pipe(
        switchMap((logged: boolean) => {
    
          return logged ? myCheck : of(!1);
        })
      )
    }
    

    【讨论】:

    • 这看起来很不错!
    • 我在这里看到的唯一缺点是myCheck 总是会被执行,即使isLogged = false 也是如此。因此,如果 myCheck 执行任何异步工作,可能会导致性能下降。
    • @BizzyBob 实际上我认为差异很小,因为使用其他方式,例如 exhaustMap 或 switchMap 需要在 isLogged 为假时将其转换为另一个 observable ......所以我认为性能方面没有区别!
    • 我同意 Sampgun 的观点。我认为这也是写我需要的最干净的方式!感谢大家的时间和支持!
    • @BizzyBob 现在我明白你的意思了。您的意思是,如果 myCheck 执行时间很长,那么当 isLogged 为 false 时,时间就会很长。我认为你是对的...我们可以使用 switchMap 进行调整然后...
    【解决方案2】:

    There is isObservable method

    示例

    const { of, iif, pipe, isObservable , operators: { mergeMap } } = rxjs;
    
    const getAsyncCheck = () => of(Boolean(Math.floor(Math.random() * 2)));
    const getSyncCheck = () => Boolean(Math.floor(Math.random() * 2));
    const getRandomBool = () => Boolean(Math.floor(Math.random() * 2));
    const getRandomCheck = () => {
      return getRandomBool() ? getAsyncCheck() : getSyncCheck();
    };
    
    
    const myFunction = () => {
    
    
      return of(getRandomBool())
        .pipe(
          mergeMap((isLogged) => {
    
            const myCheck = getRandomCheck();
            const result = isLogged ? iif(() => isObservable(myCheck), myCheck, of(myCheck)) : of(false);
            return result;
         })
       ).subscribe(result => {
        console.log(result)
      })
    };
    
    myFunction();
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"&gt;&lt;/script&gt;

    【讨论】:

    • 谢谢,我不知道isObservable 方法。凉爽的。所以基本上这种方式很好,只需使用isObservable更改检查即可。谢谢!
    【解决方案3】:

    我会强制 randomCheck 到 observable,然后使用 exhaustMap 来处理返回正确的 observable; of(false) 时,isLogged = false 或 randomCheck$ 否则:

    const myFunction = (): Observable<boolean> => {
      const randomCheck$ = isObservable(randomCheck) ? randomCheck : of(randomCheck);
    
      return getIsLogged().pipe(
        exhaustMap(isLogged => isLogged ? randomCheck$ : of(false)),
        // take(1)
      );
    };
    

    如果getIsLogged() 没有完成,我认为您需要使用take(1) 进行角防护。

    【讨论】:

    • 事实上,“isLogged”实际上是作为 Observable 返回的,这就是为什么在我的示例中它被 of 包裹的原因!
    • 返回总是可观察的不是问题,因为我实际上是在 Angular 中实现 canActivate,它接受 boolean 或 Observable&lt;boolean&gt;。
    • 我没有意识到isLogged 是可观察到的,我认为of 的使用是您在myFunction 中解决方案的一部分:-)
    • 呵呵别担心。检查了新的解决方案,绝对看起来不错!
    • 关于 take(1)(或更好的 first())的好提示,因为 isLogged$ 实际上是一个 BehaviourSubject,所以是的,它没有完成 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-24
    • 1970-01-01
    • 2018-10-23
    • 1970-01-01
    • 2019-07-27
    • 1970-01-01
    • 2021-10-27
    相关资源
    最近更新 更多