You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中如何在APP_INITIALIZER执行完成后提供自定义InjectionToken

问题核心原因

Angular 启动时会先解析所有普通提供者的依赖,若FORMATS在应用初始化阶段(比如被其他启动期服务、根组件注入),它的工厂函数会在APP_INITIALIZER的异步任务执行完成前触发,此时ConfigService里的配置还未加载完成。


解决方案

方案1:合并赋值逻辑到APP_INITIALIZER流程(改造成本最低)

适合绝大多数场景,不需要调整现有依赖结构:

  1. 先修改ConfigService,新增内部缓存属性:
@Injectable({ providedIn: "root" })
export class ConfigService {
    // 新增缓存配置的内部属性
    private formats: Formats | null = null;

    constructor() {}
    
    // 配置加载完成后调用,存储formats
    setFormats(formats: Formats) {
        this.formats = formats;
    }

    getFormats() {
        return this.formats;
    }
}
  1. 修改loadConfig工厂函数,加载完配置后先给ConfigService赋值再resolve:
export function loadConfig(configService: ConfigService, http: HttpClient): () => Promise<Config> {
    return async () => {
        // 保留你原有加载配置的逻辑,比如从接口/本地文件拉取
        const config = await http.get<Config>('/assets/config.json').toPromise();
        // 加载完成后先把formats存入ConfigService
        configService.setFormats(config.formats);
        return config;
    };
}
  1. 保留原有FORMATS的注册逻辑即可。Angular 会等所有APP_INITIALIZER的异步任务全部执行完成后,才会初始化根组件、渲染页面,也才会注入业务层用到的依赖,此时ConfigService里的formats已经赋值完成,assignFormat自然能拿到正确的值。

方案2:FORMATS需在初始化阶段被依赖的场景

如果你的FORMATS会被APP_INITIALIZER链路上的其他服务依赖,可以把FORMATS的返回值改为Promise,消费方按需等待:

  1. 先给ConfigService新增加载完成通知能力:
import { Subject } from 'rxjs';

@Injectable({ providedIn: "root" })
export class ConfigService {
    private formats: Formats | null = null;
    // 配置加载完成的通知Subject
    public configLoaded$ = new Subject<void>();

    constructor() {}
    
    setFormats(formats: Formats) {
        this.formats = formats;
        this.configLoaded$.next();
        this.configLoaded$.complete();
    }

    getFormats() {
        return this.formats;
    }
}
  1. 修改FORMATS的类型定义和工厂函数:
export const FORMATS = new InjectionToken<Promise<Formats>>("formats")

export function assignFormat(configService: ConfigService): Promise<Formats> {
    return new Promise((resolve) => {
        configService.configLoaded$.subscribe(() => {
            resolve(configService.getFormats());
        })
    });
}
  1. 消费FORMATS的地方用async管道或者await等待取值即可。

兜底强制等待方案

可以直接在FORMATS的工厂依赖中注入APP_INITIALIZER,强制等待所有初始化任务完成再执行逻辑:

providers: [{
  provide: APP_INITIALIZER,
  useFactory: loadConfig,
  deps: [HttpClient, ConfigService],
  multi: true
}, {
  provide: FORMATS,
  useFactory: async (configService: ConfigService, initializers: Promise<void>[]) => {
    // 等待所有APP_INITIALIZER的任务执行完成再返回结果
    await Promise.all(initializers);
    return configService.getFormats();
  },
  deps: [ConfigService, APP_INITIALIZER]
}]

内容的提问来源于stack exchange,提问作者frankfullstack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 16:45:03