Как заставить поток ждать завершения оператора interval () перед выполнением остальной части потока? - PullRequest
0 голосов
/ 19 июня 2020

Моя проблема заключается в следующем: мое веб-приложение использует токены аутентификации, срок действия которых может истечь. Когда срок их действия истекает, сервер отправляет ошибку 401, предлагая браузеру сначала выполнить запрос sh token refre. Я использую оператор retryWhen(), чтобы справиться с этой ситуацией. Оператор retryWhen() использует следующий код для запроса нового токена.

  public tokenExpired = ({
    maxRetryAttempts = 1
  }: {
    maxRetryAttempts?: number,
  } = {}) => (attempts: Observable<any>) => {
    return attempts.pipe(
      mergeMap((error, i) => {
        const retryAttempt = i + 1;
        if((error && error.headers && (!error.headers.get("reason-unauthorized") || !(error.headers.get("reason-unauthorized") === "authentication-token-expires-soon"))) || retryAttempt > maxRetryAttempts) {
          return throwError(error);
        }
          return this.requestNewToken().pipe(
            concatMap(refreshStatus => {
              if(refreshStatus === TokenRefreshStatus.TOKEN_REFRESHED) 
                return timer(0);
              else if(refreshStatus === TokenRefreshStatus.TOKEN_REFRESH_FAILED) 
                return throwError(error);
              else if(refreshStatus === TokenRefreshStatus.AWAIT_REFRESH && this.loggedIn.value){
                let maxWaitTimeInMilliSeconds = 4000; 
                interval(100).pipe(takeWhile(() => !(this.tokenRefreshStatus !== TokenRefreshStatus.AWAIT_REFRESH || maxWaitTimeInMilliSeconds <=0)))
                  .subscribe(value => {maxWaitTimeInMilliSeconds -= 100;});
                if(this.tokenRefreshStatus === TokenRefreshStatus.TOKEN_REFRESHED)
                  return timer(0); 
                else return throwError(error);
                }
              else 
                return throwError(error); 
            }));
      })
    );
  }

Таким образом, оператор retryWhen будет иметь tokenExpired в качестве лямбда retryWhen(tokenExpired()). Поскольку несколько запросов могут запускаться почти одновременно, я сделал код таким образом, что только первый запрос будет запускать HTTP-запрос token refre sh через this.requestNewToken(), а любой другой запрос, который делает аналогичный вызов через retryWhen, будет получить сообщение AWAIT_REFRESH от метода requestNewToken(). Таким образом я предотвращаю многократные запросы на новый токен.

Когда получено AWAIT_REFRE SH, код должен каждые 100 миллисекунд проверять, изменилось ли tokenRefreshStatus на TOKEN_REFRESHED или TOKEN_REFRESH_FAILED. Он может повторять это сейчас дольше, чем около 4000 миллисекунд. Следовательно, интервал также должен перестать повторяться, когда maxWaitTimeInMilliSeconds достигнет 0 или ниже. После этого есть оператор if, который проверяет, равно ли TokenRefreshStatus TOKEN_REFRESHED, если он это делает, он отправляет обратно timer(0), если нет, он выдает исходную ошибку, полученную оператором retryWhen.

Моя проблема в том, что приложение не ждет, пока код интервала завершится sh. Он просто переходит прямо к оператору if под этим, заставляя его всегда выдавать ошибку. Я думаю, мне не следует подписываться на интервал, но вместо этого я должен вернуть его как новую наблюдаемую, которая выполняет итерацию интервала И проверку после этого, чтобы увидеть, обновился ли токен после завершения интервала. Я просто не знаю, как мне это сделать.

Может ли кто-нибудь указать мне правильное направление? Спасибо

1 Ответ

0 голосов
/ 19 июня 2020

Моя проблема в том, что приложение не ждет, пока код интервала завершится sh.

Это происходит потому, что интервал создает наблюдаемую, которая является асинхронной. Я бы попробовал сделать вашу функцию refreshStatus asyn c для вызова await, когда вы хотите, чтобы она ждала (то есть: заключить ваш интервал в обещание ждать его):

concatMap(async refreshStatus => {
     .
     .
     .

     function checkit(){
       return new Promise((res, rej)=>{

        interval(100).pipe(takeWhile(() =>{
          if(!(this.tokenRefreshStatus !== TokenRefreshStatus.AWAIT_REFRESH || maxWaitTimeInMilliSeconds <=0)){
            return true;
          }
          else{
            res(true);
            return false;
          }
        }))
        .subscribe(value => {maxWaitTimeInMilliSeconds -= 100;});
      });
    }
    await checkit();

     .
     .
     .
})

Надеюсь, это поможет вам реши свою проблему.

...