Skip to content

Support for async service factories #20

Description

@jahudka

Hi, this library looks like a dream come true to me, I hate decorator-based DI libraries with a passion and I've wanted to get around to writing a compile-time DI implementation for literally years (in fact I tried, several times, but always fell short). So first of all, thank you from the bottom of my heart for writing and releasing this library!

I'm using MikroORM in most of my backend NodeJS apps. It's a pretty neat ORM library, I like it a lot. But its initialisation is asynchronous - and so if I want to register it as a service in a DIC, I need to either initialise it first and then register the resolved instance (which means the ORM is always initialised, even when it's not needed), or I have to type the service as a promise for the correct type. But then I want to inject this service into other services. Either I could inject the typed promise (which the dependent services cannot await in their constructor, so they would have to store a reference to the promise instead of the actual resolved service, and await that promise each time they want to access the dependency), or I have to wrap all the dependent services in a promise as well and write an async factory function for each of those services, which would do something like return new DependentService(await container.get('async-service')). This is, in fact, what I'm doing right now, because it feels like the "cleanest" solution in terms of separating the actual services from the DI implementation or the fact that some services are initialised asynchronously.

My current DI implementation is basically a lot of handwritten factories which mostly do exactly what your DI library does at runtime, except it allows for services to be async. I'm not sure how the support for generics currently works in this library - from the linked issue it seems I can use generics, but maybe the generic part of the registration is ignored..? (based on the last comment from @cmidgley, which seems to suggest that stripping the type parameter is the intended behaviour?). So if this library does support generic services, then that would probably also mean promises for services (since a promise is just a generic from this point of view). Is that a correct assumption?

And, well, further on the topic, I think that one amazing feature this library could include would be support for async services out of the box - that is, I'd like to do something like this:

container.registerSingleton<Promise<ServiceOne>>(async () => createServiceOneSomehow());
container.registerSingleton<Promise<ServiceTwo>>();

class ServiceTwo {
  constructor(private readonly one: ServiceOne) {}
}

// the transformer can now detect that "ServiceOne" is wrapped in a promise, so it should be possible to generate factory code for "ServiceTwo" which awaits the container.get() call in order to autowire the unwrapped ServiceOne instance instead of the promise

Maybe the container.register* methods might not even need the type parameter wrapped in a promise - the transformer should be able to detect that the service is async based on either the passed-in factory function, or on the fact that the service depends on other services which are async. When one wants to access such a service, the transformer could detect that container.get<ServiceTwo>() is attempting to access an async service without specifying the Promise wrapper and throw an error (since this would break at runtime)..

I'm not sure if all of this is perhaps already supported - I can't find it anywhere in the docs or issues. If it is, then that's amazing! And if it's not: is this something you'd consider adding to the library? I would very much like to offer my help in case you would.

Thanks in any case!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions