首页 > 解决方案 > rxjs6:RetryWhen在返回定时器时不起作用

问题描述

我正在构建一个小函数,它将在特定时间后刷新访问令牌(基于计算)。

public buildRefreshTokenTimerAsync(): Observable<LoginResultViewModel> {

        let accessToken = '';
        let refreshToken = '';
        let lastRefreshTokenTime: number;
        let issuedTokenTime: number;
        let accessTokenLifeTime = ACCESS_TOKEN_LIFE_TIME;
        let user: ProfileViewModel;
        let retriedTime = 0;

        // Load the last time the application refresh the access token.
        const loadLastRefreshTokenTimeObservable = this.storageMap
            .get<number>(LocalStorageKeyConstant.lastRefreshTokenAt)
            .pipe(
                tap((time: number) => {
                    lastRefreshTokenTime = time;
                })
            );

        // Load login result from storage map observable.
        const loadLoginResultObservable = this.storageMap
            .get<LoginResultViewModel>(LocalStorageKeyConstant.loginResult)
            .pipe(
                tap((loginResult: LoginResultViewModel) => {
                    if (!loginResult || !loginResult.accessToken || !loginResult.accessToken.trim()) {
                        throw new Error(ExceptionCodeConstant.accessTokenNotFound);
                    }

                    if (!loginResult.refreshToken || !loginResult.refreshToken.trim()) {
                        throw new Error(ExceptionCodeConstant.refreshTokenNotFound);
                    }

                    accessToken = loginResult.accessToken;
                    refreshToken = loginResult.refreshToken;
                    issuedTokenTime = loginResult.issuedAt;
                    accessTokenLifeTime = loginResult.lifeTime;
                    user = loginResult.user;
                })
            );

        return of(null)
            .pipe(
                flatMap(_ => {
                    return forkJoin([
                        loadLastRefreshTokenTimeObservable,
                        loadLoginResultObservable]);
                }),
                flatMap(() => {

                    // Get the current time in the system.
                    const currentTime = new Date().getTime();

                    // Calculate the time to refresh the access token.
                    const timeToRefreshToken = this.calculateAccessTokenRefreshTime(issuedTokenTime, ACCESS_TOKEN_LIFE_TIME);

                    // Access token hasn't been refreshed before.
                    if (lastRefreshTokenTime === undefined || lastRefreshTokenTime === null) {

                        // Refresh time was in the past. Refresh the token now.
                        if (currentTime >= timeToRefreshToken) {
                            return timer(0);
                        }
                    }

                    // Access token has refreshed before, however, another thread is handling the operation.
                    // Retry after the specific time.
                    if (currentTime - lastRefreshTokenTime < REFRESH_TIME_GAP) {
                        throw new Error(ExceptionCodeConstant.accessTokenIsRefreshing);
                    }

                    // Refresh time is in the future.
                    if (currentTime < timeToRefreshToken) {
                        const delayTime = timeToRefreshToken - currentTime;
                        return timer(delayTime);
                    }

                    return timer(0);
                }),
                flatMap(_ => this.loadRefreshTokenAsync(refreshToken)),
                flatMap((loginResult: LoginResultViewModel) => {
                    loginResult.user = user;
                    return this.storageMap
                        .set(LocalStorageKeyConstant.loginResult, loginResult)
                        .pipe(
                            flatMap(_ => {
                                return this.storageMap.set(LocalStorageKeyConstant.lastRefreshTokenAt, new Date().getTime());
                            }),
                            map(() => {
                                return loginResult;
                            })
                        );
                }),
                retryWhen(errors => {
                    return errors
                        .pipe(
                            flatMap(error => {
                                retriedTime++;
                                if (retriedTime > 2) {
                                    throw error;
                                }

                                if (!(error instanceof Error)) {
                                    return throwError(error);
                                }

                                if (ExceptionCodeConstant.accessTokenIsRefreshing === (error as Error).message) {
                                    return timer(2000);
                                }

                                return throwError(error);
                            })
                        );
                })
            );
    }

我期望的是当ACCESS_TOKEN_IS_REFRESHING抛出异常时,该函数将在 2 秒后重试。

如果我使用retry而不是retryWhen,该函数是可以的,但是当我更改为retryWhen并返回 atimer时,函数不会在 2 秒后重试。

我做错什么了吗?

标签: angularrxjsrxjs6

解决方案


第一的:

我正在构建一个小功能

发布带有主要缩进的 +100 行函数:D


第二:

我认为你不应该使用flatMapbut a delayWhen

retryWhen((errors) => errors.pipe(
  delayWhen((error) => {
    retriedTime++;

    if (retriedTime > 2) {
      throw error;
    }

    if (!(error instanceof Error)) {
      return throwError(error);
    }

    if (ExceptionCodeConstant.accessTokenIsRefreshing === (error as Error).message) {
      return timer(2000);
    }

    return throwError(error);
  })
))

简化的 stackblitz显示它有效


推荐阅读