Return 在满足某些条件后接收值的 Rxjs Observable

Return Rxjs Observable that receives a value after some condition is fulfilled

我正在实现一个接口,该接口具有 returns Observable 的功能。 我还需要向 Observable 传递一些值,但接收该值可能需要一些时间。

我怎样才能 return Observable 并让它等待所需的值?

更具体地说,我正在实现一个 HttpInterceptor,我想为请求设置一个令牌 header。 令牌值可能不可用,因此需要稍等(异步)并重试,直到收到该值。 然后在请求中设置tokenheader然后继续。

如何实现这样的机制?

@Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor {

  constructor(private tokenService: HttpXsrfTokenExtractor) { }

  getToken(callback) {
    let token = this.tokenService.getToken();
    if (!token) {
      // a valid token wasn't received. wait a little and try again
      setTimeout(() => {
        this.getToken(callback); //recursive call
      }, 1000);
    } else {
      // found valid token
      callback(token);
    }
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // this part should set req when a token is received, but it is asynchronous
    this.getToken((token) => {
      req = req.clone({headers: req.headers.set('X-XSRF-TOKEN', token)});
    });

    // this returns Observable. I must return Observable, but req is not ready at this point
    return next.handle(req);
  }
}

最简单的方法是使用 RxJs 运算符。使用 switchMap 应该是一个很好的解决方案。本质上,在这种情况下,switchMap 允许您将依赖的 observable 链接在一起,并且仅 return 内部 observable。它应该看起来像这样:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { 
    return getToken.pipe(
        switchMap(token => {
            req = req.clone({headers: req.headers.set('X-XSRF-TOKEN', token)});
            return next.handle(req);
        }
    );
}

请注意,您还需要将 getToken 调整为 return 可观察值才能使其正常工作。

查看代码,似乎我们有来自 getToken 的回调和来自 intercept 的观察。如果可能,最好始终使用 observable。

我们可以将 getToken(callback) 转换为使用 observable。 RxJS 有 retryWhen 运算符,我们可以用它来处理重试。

getToken() {
 const tokenFromService = of(this.tokenService.getToken()); // convert to observable

 return tokenFromService
  .pipe(
    map(token => {
      if (!token) {
        throw new Error('token is not specified'); // it will be caught by retryWhen
      }

      return token;
    }),
    retryWhen(error => {
      return error
        .pipe(
          tap(() => console.log('error happened, retry request token')), 
          delay(1000)
        )
    })
  )
}

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.getToken()
      .pipe(
        switchMap(token => {
          const modifiedReq = req.clone({headers: req.headers.set('X-XSRF-TOKEN', token)});
          return next.handle(modifiedReq);
        })
      )      
}

希望对您有所帮助

参考: