简体   繁体   English

Angular 服务构造函数在 APP_INITIALIZER 完成之前被触发

[英]Angular service constructor is being triggered before APP_INITIALIZER can finish

I seem to have a race condition between a root provided service and APP_INITIALIZER in my app, and I'm not sure how to fix it.我的应用程序中的根提供的服务和 APP_INITIALIZER 之间似乎存在竞争条件,我不知道如何解决它。

I want to ensure the APP_INITIALIZER finishes first, as the service being triggered relies on an AppConfig that the APP_INITIALIZER is supposed to set up.我想确保 APP_INITIALIZER 首先完成,因为被触发的服务依赖于 APP_INITIALIZER 应该设置的 AppConfig。 Since the constructor is triggering first it's using an undefined value and throwing an error.由于构造函数首先触发它使用未定义的值并引发错误。

I have simplified the implementation of AppInitService, but the principal is the same.我已经简化了AppInitService的实现,但是主体是一样的。 I want the promise returned by init to resolve before the UserSerialService constructor is called.我希望init返回的 promise 在调用 UserSerialService 构造函数之前解析。 Currently the constructor is being called first and is causing an error, which means the init promise never completes.目前构造函数首先被调用并导致错误,这意味着init promise 永远不会完成。

Service being constructed正在构建的服务

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

  public userSerial$: BehaviorSubject<string>;
  private _urlPrefix = '/cldflt/user/api/user/my-serial';

  constructor(private _configService: AppConfigService,
              private _http: HttpClient) {
    this.getUserSerial$().pipe(take(1)).subscribe(res => {
      this.userSerial$ = new BehaviorSubject<string>(res.body.serial)
    })
  }

  public getUserSerial$ = (): Observable<HttpResponse<Serial>> => {
    const url = this._configService.getConfig().baseUrl + `${this._urlPrefix}`;
    return this._http.get<any>(url, { observe: 'response' });
  }
}

NgModule模块

export function initApp(appInitService: AppInitService) {
  console.log("InitApp called");
  return () => appInitService.init();

}

@NgModule({
  declarations: [
  ],
  imports: [
  ],
  entryComponents: [
  ],
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: initApp,
      deps: [AppInitService],
      multi: true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

AppInitService应用初始化服务

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

  constructor(private _configService: AppConfigService,
              private _splitService: SplitIOService) { }

  init(): Promise<void>{
    console.log("InitApp called");
    return Promise.resolve().then(() => {
      console.log("Promise complete");
    });
  }
}

I would propose you to refactor your code to handle asynchronicity in right way, in that case you won't care about orde of execution;我建议你重构你的代码以正确的方式处理异步,在这种情况下你不会关心执行顺序;

class AppConfigService {
  config$ = new ReplaySubject<Configuration>(1);
  setConfig(config: Configuration) {this.config$.next(config)}
  getConfig(): Observable<Configuration> {return this.config$}
}
class UserSerialService {
  // ...
  public getUserSerial$ = (): Observable<HttpResponse<Serial>> => {
    return this._configService.getConfig().pipe(switchMap(({baseUrl}) => 
          this._http.get<any>(url, { observe: 'response' })
    );

  }
}

Can you use a Subject to emit a signal when the initialization is done and subscribe in the other service and chain the two observables together by mergeMap etc?您可以在初始化完成后使用 Subject 发出信号并订阅其他服务并通过 mergeMap 等将两个可观察对象链接在一起吗?

you could inject your _splitService later, so it would be constructed later as well as its dependency UserSerialService您可以稍后注入您的_splitService ,因此稍后将构建它以及它的依赖UserSerialService

export class AppInitService {

  constructor(private _configService: AppConfigService,
              private injector: Injector) { }

  init(): Promise<void>{
    console.log("InitApp called");
    return Promise.resolve().then(() => {
      console.log("Promise complete");
    });
  }

  doSomething() {
     const splitService = this.injector.get(SplitIOService);
     splitService.youAreNoLongerAProblem();
  }
}

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

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