首页 > 解决方案 > 在两个流之间来回切换(Observables)

问题描述

我想要完成的是,根据 cookie 的存在跟踪用户的会话。如果会话超时,我会自动注销用户。为了实现这一点,我正在等待 cookie 首先出现,即等待用户进行有效的 API 调用。然后,我开始查询 cookie,直到它不再存在。下面的代码第一次对我有用。但是,在我订阅了第一个流之后,当用户在不重新加载浏览器的情况下启动新会话时,我无法恢复它。

代码:

private _setupSession(): void {

    const waitForCookie = interval(1000)
      .pipe(
        tap(() => {
          console.log('waiting');
        }),
        switchMap(() => of(this._cookieService.get(COOKIE_STRING))),
        filter((val) => !!val),
        take(1));

    const queryCookie = interval(1000)
      .pipe(
        tap(() => {
          console.log('querying');
        }),
        switchMap(() => of(this._cookieService.get(COOKIE_STRING))),
        distinct());

    waitForCookie.pipe(concatMap(() => queryCookie))
      .subscribe(
        (val) => {
          if (!val) {
            this._router.navigate([''], {queryParams: {sessionTimedOut: true}});
          }
        },
        (err) => {
          console.log(err);
        });
  }

观察:当用户在登录页面时,我每秒都可以看到“等待”日志。用户登录后,我开始每秒看到“查询”日志。一旦会话超时,用户将返回登录页面。此时,我仍然在控制台中看到“查询”日志。一旦用户再次登录,我想做的是切换回“等待”并切换到“查询”。

标签: angularstreamrxjsobservable

解决方案


如果我了解您的要求,那么这就是您想要做的:

观察this._cookieService.get()方法中的 cookie,如果该值是null/empty/undefined然后将用户路由到登录页面,并this._cookieService.get()每隔 1 秒继续观察 on 的值。

如果我的理解是正确的,那么:

我认为你可以通过只使用这样的一个流来实现这一点:

const queryCookie = interval(1000)
      .pipe(        
        switchMap(() => of(this._cookieService.get(COOKIE_STRING))),
        //skipwhile will ensure to avoid timeout on starting of the page/app
        skipWhile(cookie => !cookie)
      );

queryCookie
      .subscribe(
        (val) => {
          if (!val) {
            this._router.navigate([''], {queryParams: {sessionTimedOut: true}});
          }
        },
        (err) => {
          console.log(err);
        });

顺便说一句 - 我建议不要每 1 秒查询一次。我猜您的应用程序可能会以某种方式在 localStorage 中将 cookie 设置为 null/empty/undefined。如果是这样,那么您可以使用 Subject/BehaviorSubject 使您的代码更好一些,并且可以避免查询 1 秒。这只是一个观察,因为我不知道您的应用程序设计。


推荐阅读