-
Notifications
You must be signed in to change notification settings - Fork 724
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Asynchronous injection of a shared module requires signature changes #475
Comments
A provider is asynchronous so there is no other way. It sounds to me like you want to execute asynchronous code as if it was synchronous but that is not a good practice. An option is to fetch that data before InversifyJS is initialized. Once you have fetched the data you can create a binding with
This is an indicator of the config being almost like a global. It makes sense to fetch it before the app starts running. |
Thanks for the reply @remojansen. What do you think? With a constant value // ./index.ts
import "reflect-metadata";
import { IConfig } from "./config";
import { Config } from "./config/config";
import { container } from "./inversify.config";
import { IService} from "./service";
import { TYPES } from "./types";
let config = new Config();
config.initialize()
.then((cfg) => container.bind<IConfig>(TYPES.IConfig).toConstantValue(cfg))
.then(() => container.get<IService>(TYPES.IService).initialize())
.catch((err) => {
console.error("fatal error", err);
process.exit(1);
});
// ./inversify.config.ts
// nothing about IConfig nor Config With a singleton // ./index.ts
import "reflect-metadata";
import { IConfig } from "./config";
import { container } from "./inversify.config";
import { IService} from "./service";
import { TYPES } from "./types";
let config = container.get<IConfig>(TYPES.IConfig);
config.initialize()
.then(() => container.get<IService>(TYPES.IService).initialize())
.catch((err) => {
console.error("fatal error", err);
process.exit(1);
});
// ./inversify.config.ts
// [import & cie]
container.bind<IConfig>(TYPES.IConfig).to(Config).inSingletonScope();
// [other bindings] |
what about having the config be blank by default, loaded with values by another class, then proceeding to start up your app? e.g. // config.ts
@injectable()
export class Config implements IConfig {
private _values: {};
constructor() {
this._values = {};
}
// stuff
load(values: {}) {
this._values = values;
}
// stuff
};
// config-loader.ts
@injectable()
export class ConfigLoader implements IConfigLoader {
constructor(@inject('IConfig') private _config) {}
async load() {
// pull config down, asynchronously
this._config.load(somedata);
}
};
// ./index.ts
const loader = container.get<IConfigLoader>(TYPES.IConfigLoader);
loader.load().then(() => {
// config is now populated, we can use classes that depend on it
});
// ./inversify.config.ts
// [import & cie]
container.bind<IConfig>(TYPES.IConfig).to(Config).inSingletonScope();
container.bind<IConfigLoader>(TYPES.IConfigLoader).to(ConfigLoader);
// [other bindings] that way the only thing you have to change is your startup script. You can also avoid having to dirty your application logic with DI code. |
Hi @ajeffrey! Your solution seems nice as well. Thx for proposing it. On my side I finally went for the singleton option (without a loader). |
Hi, I read through this issue and I wanted to check to see if anyone has found a better way to initialize a singleton with a config. This is my use case:
Is there a way for me to avoid the |
What about this solution: An injected properties blank object: bind(TYPES.settings).toConstantValue({}); And a SettingsLoader: @injectable
class SettingsLoader() {
constructor(@inject(TYPES.Settings) private readonly settings: settings) { }
async load() {
// Loads properties from external source and fills it in settings
this.settings.config1 = value1;
this.settings.config2 = value 2;
}
} this loader is injected as singleton bind(TYPES.SettingsLoader).to(SettingsLoader).inSingletonScope(); So, in the initialization of your app, you can call the load method to fill all the settings properties. |
I read carefully the documentation about provider and some other Github issues (typically #418). It seems the only way to inject an asynchronous module is to inject a provider and then call this provider to get the actual module. (For instance: http://stackoverflow.com/questions/40880447/how-to-inject-an-asynchronous-dependency-in-inversify)
However it requires the functions to be
async
and thus return a promise.In my use-case we're defining a
Config
class that needs to be initialized asynchronously (typically, the config will fetch values from an external service). Almost all the other modules require this config. Therefore it is not a viable solution to change all the public methods into async methods just to be able to use theawait this.init();
trick.I took the Inversify basic example and I extended it to show this use-case:
https://github.com/Pyo25/inversify-basic-example/tree/asynchronous_config_dependency
Particularly, the
EpicBattle
class has to return aPromise<string>
but that something we should avoid. (cfr: https://github.com/Pyo25/inversify-basic-example/blob/asynchronous_config_dependency/src/entities/battle/epic_battle.ts)So, my questions are:
Thanks for your help
The text was updated successfully, but these errors were encountered: