简体   繁体   English

在不使用异步等待关键字的情况下等待 promise 的值?

[英]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.在我的 Angular HttpInterceptor class 中,我正在实现一种刷新机制,用于检测 JWT 令牌何时过期,并从后端检索一个新的令牌。 Once the new one is retrieved it overwrites the token in local storage, thus extending the expiration of the user's session.一旦检索到新的令牌,它将覆盖本地存储中的令牌,从而延长用户 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. HttpInterceptor 的 function 的一部分是如果检测到 JWT 到期日期已到,则将用户从其 session 中注销。

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.在发出对 JWT 的请求后,似乎马上又向后端发送了另一个请求。 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.由于此时令牌已过期,因此 API 返回 403,并且拦截器正在检测到这一点并将用户踢出 session,然后新的 Z1D1FADBD9150349C135781140F 令牌的响应已返回。 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.通常这不是问题,因为我可以使用 async/await 来确保在发出任何其他请求之前返回新的 JWT。 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>>> .但是,这在拦截器中不起作用,因为它实现了HttpInterceptor接口,这意味着如果我尝试在接口实现的intercept方法上使用async关键字,那么我会得到一个编译器错误,因为intercept方法的返回类型必须是Observable<HttpEvent<T>>并且不能是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.因此,我需要一种方法来等待 this.getNewToken this.getNewToken() promise 的返回值,而不使用 async/await 关键字。 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.您可以从 promise 创建一个 observable,并使用mergeMap运算符合并您要返回的 observable。


  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.你不应该使用 Promise,而应该使用 observables。

Anyway, simply wrap it around a promise that you resolve when your data is loaded.无论如何,只需将它包裹在一个 promise 上,您在加载数据时解决该问题。

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.正如@mbojko指出的那样,返回一个可观察对象(就像我首先说的那样),因为签名不接受承诺。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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