首页 > 解决方案 > Rxjs - 在等待方法执行时阻止后续请求

问题描述

我有一个类,UserPermissionsService它在最初被访问时发出一个服务器请求。请求完成后,后续调用将使用存储在 a 中的数据BehaviorSubject来完成其操作。但是,如果多个同时发生,该init方法将不会完成,并且会发生多个服务器请求。

如何重构以下类以强制后续调用init在执行之前等待初始请求完成?


@Injectable({
  providedIn: 'root'
})
export class UserPermissionsService implements OnDestroy{
  private _permissions = new BehaviorSubject<AppliedPermissions>(null);
  public permissionSnapshot: AppliedPermissions;
  public permissions: Observable<AppliedPermissions> = this._permissions.asObservable();

  constructor(private _userService: UserService) {
  }

  init(): Observable<AppliedPermissions> {
    return this._userService.getPermissions()
      .pipe(tap(p => {
        this._permissions.next(p)
        this.permissionSnapshot = p;
      }));
  }

  hasPermission(permission: string): Observable<boolean> {
    return this._permissions.pipe(
      switchMap(value => value ? of(value) : this.init()),
      map(response => {
        const perm = response.permissions
          .find(el => el.permissionName === permission);

        if (!perm)
          return false;

        return perm.allow;
      }),
      catchError(_ => of(false))
    );
  }

  inRole(role: string): Observable<boolean> {
    return this._permissions.pipe(
      switchMap(value => value ? of(value) : this.init()),
      map(response => {
        return !!response.roles.find(el => el === role);
      }),
      catchError(_ => of(false))
    );
  }

  ngOnDestroy(): void {
    this._permissions?.complete();
  }
}

标签: javascripttypescriptrxjsrace-condition

解决方案


你考虑过OnInit()

export class App implements OnInit {
  constructor() {
     // Called first time before the ngOnInit()
  }    
  ngOnInit() {
     // Called after the constructor and called after the first ngOnChanges() 
  }
}

现在回答您的问题 - 尝试flatmap保留/控制顺序,这样可以确保保留执行顺序。例如看这里

first
  .flatMap(() => second)
  .flatMap(() => third)
  .subscribe(()=> console.log('finished'));

另外的选择

// use this wrapper 
function WaitForComplete<T>(signal: Observable<any>) {
    return (sourceInit: Observable<T>) => signal.pipe(
        first(), switchMap(_ => sourceInit),
    );
}

// force the waiting to complete, by deferring the subscribe on the first source 
// till the signal observable completes/emits (many versions of this out there)
var YourSecond = 
  anotherObservable.pipe(WaitForComplete(YourFirstObservableFunction), take(1));

更新:服务可以实现ngOnInit()

在此处输入图像描述


推荐阅读