AuthorizeService isAuthenticated () subscribe wird mehrmals mit rxjs concat aufgerufen

Aug 31 2020

Ich verwende einen standardmäßigen clientseitigen Authentifizierungsdienst aus einer Visual Studio-Vorlage.

Es gibt ein Typoskript AuthorizeService mit einer Funktion namens isAuthenticated, die die folgende Funktion aufruft und prüft, ob sie null ist oder nicht.

Die getUser-Funktion:

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

wenn .subscribe für die obige Funktion aufgerufen wird. Abonnieren wird dreimal aufgerufen. Vermutlich für jede in der Concat-Funktion beobachtbare. Wenn ich getUser oben verwende, würde ich erwarten, dass subscribe einmal aufgerufen wird. Wie würde ich das erreichen?

Ich habe versucht, die oben genannten in verschachtelte Versprechen umzuwandeln, die einen Wert zurückgeben, aber ohne Erfolg, da aus irgendeinem Grund nach der Rückgabe des Ergebnisses die Auflösung (null) zurückgegeben wird, obwohl der Benutzer im Sitzungsspeicher vorhanden ist

Antworten

1 MrkSef Aug 31 2020 at 23:29

Ich gehe davon aus, dass getUser () nur einen Benutzer zurückgeben soll. Im Moment lautet Ihre Logik "Holen Sie sich den aktuellen Wert von userSubject und holen Sie einen Benutzer aus dem Speicher und rufen Sie auch den aktuellen und alle zukünftigen Werte von userSubject ab."

Dies bedeutet, dass Sie mindestens 3 Benutzer zurückerhalten, wenn der Wert von userSubject wahr ist. Das sagt deine Logik.

Ich bin mir nicht sicher, was Sie unter "nur einmal abonnieren" verstehen, aber ich gehe davon aus, dass Sie "nur 1 Benutzer zurückgeben" meinen. Eine einfache Möglichkeit, dies zu tun, besteht darin, nur einen Wert aus Ihrem Concat-Aufruf zu übernehmen:

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

Dies kann zu unvorhersehbarem Verhalten führen. Welcher der drei Streams zuerst einen Wert ausgibt, ist der Wert, den Sie annehmen. Wenn der Abschluss von getUserFromStorage () einige Zeit in Anspruch nimmt, erhalten Sie immer null zurück. Ich vermute, das passiert dir, wenn du Versprechen verschachtelst (obwohl ich deinen Code sehen müsste, um sicher zu sein).

Der bessere Weg, dies zu erreichen, ist mit switchMap oder mergeMap (beides funktioniert in diesem Fall). Ich vermute auch, dass Sie einen Benutzer nur aus dem Backend holen möchten, wenn es keinen im userSubject gibt. Dieser Ansatz würde den aktuell authentifizierten Benutzer effektiv zwischenspeichern.

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

Was macht das? Es wird nur versucht, einen Benutzer aus dem Speicher zu holen, wenn der Benutzer von userSubject nicht der Wahrheit entspricht. this.getUserFromStorage () wird niemals aufgerufen (oder abonniert), wenn sich ein Benutzer im userSubject befindet. Zu beachten ist, dass der zweite Aufruf von (1) nicht erforderlich ist, wenn getUserFromStorage () immer nur einen Wert zurückgibt. Dies setzt auch voraus, dass getUserFromStorage () null zurückgibt, wenn sich kein Benutzer im Speicher befindet.

Schließlich habe ich alle Filter entfernt, da es scheint (aus Ihrer Beschreibung), dass dieser Stream eine Null zurückgeben soll, wenn sich kein Benutzer im Betreff und kein Benutzer im Speicher befindet. Wenn wir eine Null-Rückgabe herausfiltern, geben wir niemals Null zurück. Stattdessen habe ich nur null zurückgegeben, wenn getUserFromStorage () null zurückgibt.

RafiHenig Aug 31 2020 at 21:35

Verwenden Sie forkJoinstattdessen (Wenn alle Observablen abgeschlossen sind, geben Sie den zuletzt emittierten Wert von jedem als Array aus), wie unten gezeigt:

Hinweis: Ich habe takedem letzten Observable einen Operator hinzugefügt , damit es abgeschlossen wird

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