【问题标题】:Angular function relies on result of async function角函数依赖于异步函数的结果
【发布时间】:2020-05-28 13:02:17
【问题描述】:

设置:

我有一个函数 isAuthorized(),在一个单例 AuthSessionSingleton 中,它依赖于异步函数的结果。

异步函数是一个api调用,在构造函数中调用,它期望返回一个对象isAuthorized() 依赖于那个对象


问题:

在后端调用解析之前,某些组件会立即调用函数isAuthorized()。 我希望能够在 isAuthorized() 函数内等待,直到后端调用解决并且 object 不再为空。


简单代码:

//auth.singleton.ts
@Injectable({providedIn: 'root'})
export class AuthSessionSingleton {
  userInfo: UserInfo = null;
  constructor(private service: MyService) {
    this.loadUserInfo();
  }
  
  loadUserInfo() {
    this.service.makeBackendCall().subscribe({
      next: (obj: UserInfo) => {
        this.userInfo = obj;
      }
    });
  }
  
  isAuthorized() {
    if(this.userInfo == null) {
      // do something to wait asynchronously
    }

    // Very simple example
    if(this.userInfo.Role == 'Admin') {
      return true;
    }
    return false;
  }
}
//some.component.ts
export class SomeComponent {
  authorized: boolean = false;
  
  constructor(private auth: AuthSessionSingleton) {
    this.checkAuthorization();
  }
  
  async checkAuthorization() {
    this.authorized = await this.auth.isAuthorized();
  }
}

在上面的简化程序中,调用 isAuthorized() 的 SomeComponent 可能会在 userInfo 仍然为空时检查它。我需要等待它不为空。


可能的解决方案:

到目前为止,我已经确定了许多解决方案。我正在寻找最正确的解决方案,希望是我从未想过的解决方案。

1。收听订阅

isAuthorized()if(this.userInfo==null) 订阅与loadUserInfo() 相同的订阅并等待它。

2。等到发出信号

我可以在 AuthSessionSingleton 中创建一个 Subject 并从 SomeComponent 订阅,等到我收到信号,然后调用 IsAuthorized()

这似乎特别迂回。

3。等一下

有很多方法可以让我旋转我的齿轮,直到我需要的对象不再为空。

while(this.userInfo == null) {
  // do something
}

请求:

我想要一种在isAuthorized() 中异步等待直到this.userInfo != null 的方法。可以对isAuthorized() 进行任意数量的调用(来自不同的组件),并且所有调用都应该等到该值被解析。

有什么想法吗?


编辑

我现在意识到一个更准确的用例改变了可能的解决方案。通过接受一组值进行循环,它变得更加复杂。

isAuthorized(roles: string[]) {
  let(i = 0; i < roles.length; i++) {
    let role = roles[i];
    if(this.userInfo.Role == role) {
      return true;
    }
    return false;
  }
}

【问题讨论】:

  • 我会使用解决方案 2,但您可以使 isAuthorized() 函数异步并在调用完成后返回状态。在解决方案 1 中,如果您订阅相同的端点,您将触发额外的不需要的 HTTP 请求。如果后端不回复,解决方案 3 可能会导致无限循环。
  • 如果你的应用在你的 api 调用完成之前不初始化会好吗?

标签: angular asynchronous async-await rxjs singleton


【解决方案1】:

当涉及一个异步操作时,您可以使其所有依赖项异步。试试下面的

auth.singleton.ts

@Injectable({providedIn: 'root'})
export class AuthSessionSingleton {
  userInfo = new BehaviorSubject<UserInfo>(null);

  constructor(private service: MyService) {
    this.loadUserInfo();
  }

  loadUserInfo() {
    this.service.makeBackendCall().subscribe({
      next: (obj: UserInfo) => {
        this.userInfo.next(obj);
      }
    });
  }

  isAuthorized() {
    const result = new Subject<boolean>();

    this.userInfo.pipe(take(1)).subscribe(
      userInfo => {
        if (userInfo == 'Admin') {
          result.next(true);
        } else {
          result.next(false)
        }
      }
    );

    return result.asObservable();
}

现在您可以订阅组件中的isAuthorized() 函数。您也可以公开userInfo 以检查组件中的授权。

组件

ngOnInit() {
  this.authService.isAuthorized().subscribe(
    status => {
      if (status) {
        // authorized
      } else {
        // unauthorized
      }
    }
  );
}

如 cmets 中所述,我不喜欢解决方案 1 和 2,原因如下

  1. 解决方案 1:订阅可能会触发额外的不需要的 HTTP 请求。
  2. 解决方案 3:如果后端不回复,可能会导致无限循环。

【讨论】:

  • 我很欣赏彻底的解决方案。我添加了一个编辑以显示更复杂的情况,我现在意识到这会使您的解决方案更难以实施。
【解决方案2】:

以下代码将确保您的应用仅在您的 loadUserInfo 完成后初始化。所以其他组件只有在应用初始化并且你的用户信息已经存在时才会调用 isAuthenticated。

在你的 AppModule 中有这个:-

{
      provide: APP_INITIALIZER,
      useFactory: init_app,
      deps: [AuthSessionSingleton],
      multi: true
 }

 export function init_app(authService: SessionService) {
  return () => authService.loadUserInfo();
 }

改变你的 AuthSessionSingleton :-

@Injectable({providedIn: 'root'})
export class AuthSessionSingleton {

  constructor(private service: MyService) {
  }

  loadUserInfo() {
    return this.service.makeBackendCall().pipe(map((obj: UserInfo) => {
        this.userInfo.next(obj);
    })).toPromise();
  }

   isAuthorized() {
      if(this.userInfo == null) {
          // do something to wait asynchronously
      }

      // Very simple example
      if(this.userInfo.Role == 'Admin') {
        return true;
      }
      return false;
   }
}

【讨论】:

  • 虽然这可行,但它似乎违背了使用异步调用并允许其他一切同时继续工作的目的。
  • 但它会让你的应用在任何地方都稳定。因为用户信息是主要的核心要求,对吧?
猜你喜欢
  • 1970-01-01
  • 2014-07-08
  • 2021-08-01
  • 1970-01-01
  • 2022-07-23
  • 1970-01-01
  • 1970-01-01
  • 2016-02-10
  • 2022-08-22
相关资源
最近更新 更多