Repository navigation
Fluxject Version 1.2
Fluxject v1.2 is a re-write of the project, as the original vision did not adhere to what an IoC library should look like. Much of Fluxject's flaws revolved around the fact that it did not support lazy instantiation-- and thus, circular dependencies were super common.
With the rewrite, Fluxject now works off of Lazy Instantiation at its core, but a few features had to be removed to support lazy instantiation.
Here's a summary of what Fluxject v1.2 introduces for the consumers of this library.
fluxject() vs. Container.create()
You can now use import { fluxject } from "fluxject"; and use that in place of Container.create(). While this is a minor change, I feel like it was necessary for simplifying the usage of this library.
Deprecation of Async Services
Asynchronous services are no longer supported, as the way JavaScript handles promises, there is no way to defer the instantiation of an asynchronous service while also handling the "waiting" part behind prepare() or createScope(). So now, all services that return a promise must be awaited when they are retrieved.
Deprecation of Static Value Services
It is no longer possible to register static value services. This behavior may return in future versions, but for now, it is best to hold static values in its own Singleton service.
Lazy Instantiation
All services are proxied through a new class type, LazyReference<T>, which will defer the instantiation of the service until it is explicitly used by the consumer.
See the below excerpt for details:
import { fluxject } from "fluxject";
class ServiceA {
#serviceB;
constructor({ serviceB }) {
console.log("Instantiated ServiceA");
// In v1.0, this would trigger the instantiation of serviceB
// in v1.1, serviceB would have already been instantiated
// in v1.2, serviceB is still not instantiated. serviceB is, instead, a LazyReference
this.#serviceB = serviceB;
// in v1.2, any de-reference of serviceB would trigger the instantiation, like so:
serviceB.doSomething();
}
}
class ServiceB {
#serviceA;
constructor({ serviceA }) {
console.log("Instantiated ServiceB");
this.#serviceA = serviceA;
}
doSomething() {
console.log("Did something!");
}
}
const container = fluxject()
.register(m => m.singleton({ serviceA: ServiceA, serviceB: ServiceB }));
const provider = container.prepare();
console.log("Getting serviceA from provider");
provider.serviceA; // this would do nothing
console.log("De-referencing serviceA");
provider.serviceA.toString(); // this would count as a property de-reference
// so would this
// "toString" in provider.serviceA;
// and this
// provider.serviceA.someRandomVariable = 10;
// and also this
// provider.serviceA instanceof ServiceA;
console.log("Done de-referencing serviceA!");The above program would print the following
Getting serviceA from provider
De-referencing serviceA
Instantiated ServiceA
Instantiated ServiceB
Did something!
Done de-referencing serviceAA few things to note about the above example:
- While it may look like a circular dependency would occur (and it would have in previous versions of Fluxject), since the services are lazily instantiated, there would be no circular dependency! Be careful, though, if the
ServiceBconstructor de-referenced some property on the injected service,serviceA, then a circular dependency would occur. - While circular dependencies usually mean poor design in the application, sometimes they may slip past with trivial solutions. In previous versions of fluxject, the error thrown when circular dependencies occurred was not so informative. Fluxject v1.2 detects these circular dependencies, and will throw a user-friendly stack-traced
CircularDependencyErrorin its place.
Disposal Changes
In previous versions of Fluxject, calling the dispose() method on the HostServiceProvider would call all [Symbol.dispose] or [Symbol.asyncDispose] methods on all registered singleton services. While this wouldn't seem like an issue, it was missing one big piece-- ScopedServiceProviders were never disposed of as well. This was mainly an issue because it could cause erroneous behavior within scoped services, where they may attempt to access a Singleton or Transient service, but those services have been disposed of. Now, in v1.2, Fluxject will invoke [dispose() on all scoped service providers created from the host service provider, so that way all scoped services are also disposed of properly.