AuthorizeService isAuthenticated () subscribe вызывается несколько раз с помощью rxjs concat

Я использую службу аутентификации на стороне клиента по умолчанию из шаблона Visual Studio.

Существует машинописный текст AuthorizeService, в котором есть функция isAuthenticated, которая вызывает указанную ниже функцию и проверяет, является ли она нулевой или нет.

Функция getUser:

public getUser(): Observable<IUser> {
    return concat(
      this.userSubject.pipe(take(1), filter(u => !!u)),
      this.getUserFromStorage().pipe(filter(u => !!u), tap(u => this.userSubject.next(u))),
      this.userSubject.asObservable());
  }

когда .subscribe вызывается для указанной выше функции. Подписка вызывается трижды. Предположительно для каждого наблюдаемого в функции concat. Используя getUser выше, я ожидаю, что метод subscribe будет вызван один раз. Как мне этого добиться?

Я попытался преобразовать приведенное выше во вложенные обещания, которые возвращают одно значение, но безуспешно, поскольку по какой-то причине после возврата результата он возвращает разрешение (null), даже если пользователь существует в хранилище сеанса


person John Mcdowl    schedule 31.08.2020    source источник
comment
Должен ли getUser () действительно генерировать только один раз, или же возвращаемый наблюдаемый объект должен испускать текущего пользователя всякий раз, когда пользователь изменяется?   -  person frido    schedule 31.08.2020


Ответы (2)


Я предполагаю, что вы хотите, чтобы getUser () возвращал только одного пользователя. Прямо сейчас ваша логика говорит: Получить текущее значение userSubject и получить пользователя из хранилища, а также получить текущие и все будущие значения userSubject.

Это означает, что если значение userSubject истинно, вы вернете как минимум 3 пользователей. Об этом говорит ваша логика.

Я не совсем понимаю, что вы имеете в виду под «подпиской только один раз», но предполагаю, что вы имеете в виду «вернуть только 1 пользователя». Один простой способ сделать это - взять только одно значение из вашего вызова concat:

return concat(...).pipe(take(1));

Это может привести к непредсказуемому поведению. Какой из трех потоков передаст значение первым, будет значением, которое вы примете. Если для завершения getUserFromStorage () требуется некоторое время, вы всегда получите значение null. Я предполагаю, что это то, что происходит с вами, когда вы вкладываете обещания (хотя мне нужно увидеть ваш код, чтобы быть уверенным).

Лучше всего это сделать с помощью switchMap или mergeMap (в этом случае любой из них будет работать). Я также предполагаю, что вы хотите получить пользователя только из бэкэнда, если его нет в userSubject. Такой подход будет эффективно кэшировать аутентифицированного в данный момент пользователя.

public getUser(): Observable<IUser> {
  return this.userSubject.pipe(
    take(1),
    mergeMap(u => {
      if(u) return of(u);
      return this.getUserFromStorage().pipe(
        tap(u => u && this.userSubject.next(u))
      );
    }), 
    take(1)
  );
}

Что это значит? Он пытается получить пользователя из хранилища, только если пользователь из userSubject не соответствует действительности. this.getUserFromStorage () никогда не вызывается (или на него подписывается), если в userSubject есть пользователь. Следует отметить, что второй вызов take (1) не нужен, если getUserFromStorage () когда-либо возвращает только одно значение. Это также предполагает, что getUserFromStorage () возвращает null, если в хранилище нет пользователя.

Наконец, я удалил все фильтры, поскольку кажется (из вашего описания), что вы хотите, чтобы этот поток возвращал нуль, если в теме нет пользователя и нет пользователя в хранилище. Если мы отфильтруем нулевой возврат, мы никогда не вернем нулевое значение. Вместо этого я сделал то, что мы возвращаем null, только если getUserFromStorage () возвращает null.

person Mrk Sef    schedule 31.08.2020
comment
В итоге я инициализировал пользователя при запуске приложения и просто возвращал this.userSubject.asObservable () для последующих запросов. - person John Mcdowl; 03.09.2020

Вместо этого рассмотрите возможность использования forkJoin (когда все наблюдаемые будут завершены, испустите последнее переданное значение из каждого в виде массива), как показано ниже:

Примечание: я добавил оператор take к последней наблюдаемой, чтобы она была завершена

public getUser(): Observable <[IUser, IUser, IUser]> {
  return forkJoin(
    this.userSubject.pipe(take(1), filter(u => !!u)),
    this.getUserFromStorage().pipe(filter(u => !!u), tap(u => this.userSubject.next(u))),
    this.userSubject.asObservable().pipe(take(1))
  );
}
person Rafi Henig    schedule 31.08.2020
comment
Спасибо за быстрый ответ. С вышесказанным функция никогда не разрешает / подписывается / завершает - person John Mcdowl; 31.08.2020
comment
похоже, вам нужен только getUserFromStorage - person Rafi Henig; 31.08.2020
comment
Это BehaviorSubject - person John Mcdowl; 31.08.2020