Angular中初始化互相依赖的两个服务时出现数据库未初始化错误的问题求助
我完全懂你碰到的这个坑——在Angular启动阶段,你本来想先把数据库服务初始化好,再让依赖它的业务服务启动,但因为初始化器的并行执行逻辑,导致ItemsService去读数据库的时候,DatabaseService的异步初始化还没跑完,直接触发了Database not initialized yet的报错。
问题根因分析
Angular的provideAppInitializer会并行执行所有注册的初始化器,而且你的initDatabase是异步函数(返回Promise),但原来的写法里两个初始化器之间没有任何等待关系:
- 第一个初始化器触发
initDatabase后就进入异步执行流程 - 第二个初始化器几乎同时就触发了
initItemsService,这时候数据库的异步初始化大概率还没完成,调用getDatabase()自然就抛出错误了
解决方案
这里有两个简单可靠的解决思路,你可以根据自己的代码风格选:
方案一:合并初始化逻辑,严格保证执行顺序
把两个服务的初始化逻辑合并到同一个初始化器里,通过await等待数据库初始化完成后,再初始化ItemsService。这样能100%保证顺序:
export const appConfig: ApplicationConfig = { providers: [ provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideAnimations(), MessageService, DatabaseService, ItemsService, // 合并成一个初始化器,先等数据库就绪再初始化业务服务 provideAppInitializer(async () => { const dbService = inject(DatabaseService); // 等待数据库异步初始化完成 await dbService.initDatabase(); const itemsService = inject(ItemsService); itemsService.initItemsService(); }) ], };
方案二:让业务服务主动等待数据库初始化
修改ItemsService的initItemsService方法,让它内部先确保数据库初始化完成,再执行后续逻辑。甚至可以给DatabaseService加一个初始化完成的信号,让依赖服务能自动监听这个状态:
首先优化DatabaseService,添加初始化完成的通知:
@Injectable({ providedIn: 'root', }) export class DatabaseService { private static database: MyDatabase; // 用ReplaySubject保存初始化信号,确保晚订阅的也能收到通知 private static readonly initialized$ = new ReplaySubject<void>(1); constructor() {} async initDatabase(): Promise<void> { console.log('Initializing database'); if (!DatabaseService.database) { const db = await createRxDatabase<MyDatabaseCollections>({ name: 'mydb', storage: getRxStorageDexie(), }); await db.addCollections({ items: { schema: itemSchema }, baseUnits: { schema: baseUnitSchema }, brands: { schema: brandSchema }, contacts: { schema: contactSchema }, suppliers: { schema: SupplierSchema }, }); DatabaseService.database = db; // 发送初始化完成的信号 DatabaseService.initialized$.next(); } // 确保调用者能等待初始化完成(不管是第一次还是已初始化过) return DatabaseService.initialized$.pipe(take(1)).toPromise(); } getDatabase(): MyDatabase { if (!DatabaseService.database) { throw new Error('Database not initialized yet'); } return DatabaseService.database; } }
然后修改ItemsService的初始化方法:
async initItemsService() { // 先等待数据库初始化完成,再执行后续逻辑 await this.databaseService.initDatabase(); // 现在数据库肯定就绪了,放心订阅数据 this.databaseService.getDatabase().items.find().$.subscribe((docs) => { this.setItems(docs); }); }
这样就算初始化器还是并行执行,initItemsService内部会自动等数据库准备好,不会再触发报错。
额外小建议
你还可以考虑把ItemsService里的订阅逻辑和数据库初始化信号绑定,比如在构造函数里就监听数据库的初始化完成事件,自动启动订阅——这样甚至能去掉initItemsService方法,让服务的启动逻辑更“自驱”。
备注:内容来源于stack exchange,提问作者Kadiem Alqazzaz

