简体   繁体   中英

Awaiting the value of a promise without using the async await keywords?

In my Angular HttpInterceptor class I'm implementing a refresh mechanism that detects when the JWT token is expired, and retrieves a new one from the backend. Once the new one is retrieved it overwrites the token in local storage, thus extending the expiration of the user's session.

Part of the HttpInterceptor's function is to log the user out of their session if it detects that the JWT expiry date has been reached.

However I am coming across a problem. After the request for the JWT has been sent out, it seems that another request is being sent out to the backend immediately after. Since the token is expired by this point, the API is returning a 403 and the interceptor is detecting this and kicking the user out the session before the response with the new JWT token has returned. This means that the user is being logged out before the token has had a chance to be refreshed.

Usually this wouldn't be an issue, as I could just use async/await to ensure that the new JWT is returned before any other request is made. However this will not work in the interceptor as it implements the HttpInterceptor interface, which means that if I attempt to use the async keyword on the intercept method that the interface implements, then I get a compiler error as the return type of the intercept method must be Observable<HttpEvent<T>> and cannot be Promise<Observable<HttpEvent<T>>> .

//The below violates the HttpInterceptor interface
async intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Promise<Observable<HttpEvent<any>>> { ... }

//The below violates the async keyword
async intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> { ... }

My current code for the intercept method is below.

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    req = this.addHeaders(req);

    if(req.headers.has("refreshTokenRequest")) {
      return next.handle(req).pipe(tap(val => console.log("Interceptor handler, req to: " + req.url)));
    }

    if(localStorage.getItem("jwt_token")) {
      if(util.isJwtExpired("jwt_token")) {
        console.log("Jwt is expired, getting new token");
        this.getNewToken().then(newTokenResponse => {  //the .then() method needs to be awaited so that the expired JWT token can be overwritten before ANY other requests are sent
          this._authService.setAuthorizationToken(newTokenResponse.token);
          console.log("New token set");
          return this.handleRequestWithCatchError(req, next);
        });

      }
    }

    return this.handleRequestWithCatchError(req, next);
  }

So therefore I need a way to be able to await the returned value of the this.getNewToken() promise, without using the async/await keywords. Is there a way to do this?

You can create an observable from promise, and use mergeMap operator to merge the observables you want to return.


  import { from } from 'rxjs';
  import { tap, mergeMap } from 'rxjs/operators';

  ...

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    req = this.addHeaders(req);

    if(req.headers.has("refreshTokenRequest")) {
      return next.handle(req).pipe(tap(val => console.log("Interceptor handler, req to: " + req.url)));
    }

    if(localStorage.getItem("jwt_token")) {
      if(util.isJwtExpired("jwt_token")) {
        console.log("Jwt is expired, getting new token");
        return from(this.getNewToken.call(this)).pipe(
          tap(newTokenResponse => {
            this._authService.setAuthorizationToken(newTokenResponse.token)
          }),
          mergeMap(() => this.handleRequestWithCatchError(req, next))
        )

      }
    }
    return this.handleRequestWithCatchError(req, next);
  }

You shouldn't use promises but rather observables.

Anyway, simply wrap it around a promise that you resolve when your data is loaded.

req = this.addHeaders(req);
const prom = new Promise((resolve, reject) => {
  if(req.headers.has("refreshTokenRequest")) {
  // Your code here
  }
})

return prom.then(v => this.handleRequestWithCatchError(req, next));

EDIT

As @mbojko pointed out, return an observable instead (like I've said first), because the signature doesn't accept promises.

return from(prom.then(v => this.handleRequestWithCatchError(req, next)));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM