Search code examples
rxjsobservableangular-http-interceptors

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


I'm implementing an interface that has a function that returns Observable. I also need to pass some value to the Observable, but it may take some time to receive that value.

How can I still return the Observable and also make it wait for the needed value?

To be more specific, I'm implementing an HttpInterceptor and I want to set a token to the request header. The token value could be unavailable, so need to wait a little (asynchronously) and try again, until the value is received. Then set the token in the request header and continue.

How can I implement such mechanism?

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

Solution

  • The easiest thing to do is use RxJs operators. Using switchMap should be a good solution here. Essentially in this case, switchMap allows you to chain dependent observables together and only return the inner observable. It should look something like this:

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

    Please note, You'll need to adjust your getToken to return an observable as well in order for this to work.