AuthorizeService isAuthenticated () subscribe se llama varias veces con rxjs concat
Estoy usando un servicio de autenticación del lado del cliente predeterminado de una plantilla de Visual Studio.
Hay un AuthorizeService de mecanografiado que tiene una función llamada isAuthenticated que llama a la función siguiente y verifica que sea nula o no.
La función 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());
}
cuando se llama a .subscribe en la función anterior. Suscribirse se llama tres veces. Presumiblemente para cada observable en la función concat. Usando getUser arriba, esperaría que se llamara a subscribe una vez. ¿Cómo lo lograría?
Intenté convertir lo anterior en promesas anidadas que devuelven un valor pero sin éxito, ya que por alguna razón, después de que se devuelve el resultado, devuelve resolve (nulo) aunque el usuario existe en el almacenamiento de sesión
Respuestas
Supongo que desea que getUser () solo devuelva un usuario. En este momento, su lógica dice "Obtenga el valor actual de userSubject y obtenga un usuario del almacenamiento y también obtenga los valores actuales y futuros de userSubject".
Esto significa que si el valor de userSubject es verdadero, obtendrá al menos 3 usuarios de vuelta. Esto es lo que dice tu lógica.
No estoy seguro de lo que quiere decir con 'solo suscribirse una vez', pero asumiré que quiere decir 'solo devuelve 1 usuario'. Una forma sencilla de hacerlo es tomar solo un valor de su llamada concat:
return concat(...).pipe(take(1));
Esto puede provocar un comportamiento impredecible. Cualquiera de las tres corrientes que emita un valor primero será el valor que tome. Si getUserFromStorage () tarda algún tiempo en completarse, siempre obtendrá un valor nulo. Supongo que esto es lo que le sucede cuando está anidando promesas (aunque tendría que ver su código para estar seguro).
La mejor manera de lograr esto es con switchMap o mergeMap (cualquiera de los dos funcionará en este caso). También supongo que solo desea obtener un usuario del backend si no hay uno en userSubject. Este enfoque almacenaría en caché de manera efectiva el usuario actualmente autenticado.
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)
);
}
¿Qué hace esto? Intenta obtener un usuario del almacenamiento solo si el usuario de userSubject no es veraz. this.getUserFromStorage () nunca se llama (ni se suscribe) si hay un usuario en userSubject. Es de destacar que la segunda llamada a take (1) no es necesaria si getUserFromStorage () solo devuelve un valor. Esto también supone que getUserFromStorage () devuelve nulo si no hay ningún usuario en el almacenamiento.
Finalmente, eliminé todos los filtros ya que parece (de su descripción) que desea que esta transmisión devuelva un valor nulo si no hay ningún usuario en el Asunto ni en el almacenamiento. Si filtramos un retorno nulo, nunca devolveremos un valor nulo. Lo que hice en su lugar es que solo devolvemos nulo si getUserFromStorage () devuelve nulo.
Considere usar en su forkJoinlugar (cuando todos los observables se completen, emita el último valor emitido de cada uno como una matriz) como se muestra a continuación:
Nota: he agregado el takeoperador al último observable para que se complete
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))
);
}