owja / ioc

:unicorn: lightweight (<1kb) inversion of control javascript library for dependency injection written in typescript
MIT License
287 stars 13 forks source link

Async construction? #59

Open benjamingr opened 2 years ago

benjamingr commented 2 years ago

Hey, I might need to download a bundle containing a service when resolving it so I need a way to .get asynchronously and have async factories (like in InversifyJS for example).

Alternatively if plugins could return a Promise I guess that would also work for this use case.

hbroer commented 2 years ago

Hey, the dependency/service can be a Promise itself, doesn't that help?

const TYPE = {
    Dependency: token<Promise<Dependency>>("dependency"),
};

container.bind<Dependency>(TYPE.Dependency)).toFactory(async () => {
  const dependency = new Dependency();
  await dependency.someAsyncFunction();
  return dependency;
});
benjamingr commented 2 years ago

How would that work with recursive dependencies?

hbroer commented 2 years ago

Recursion should work as normal if you don't access the constructor in a constructor, or access a function in a factory which calls a function of the depending service. But that's always a problem.

async function one() { await two(); }
async function two() { await one(); }

one();

I never had a usecase where I needed a async service. I use Promise a lot but not on that level. Only where needed in methods or getter.

benjamingr commented 2 years ago

I never had a usecase where I needed a async service. I use Promise a lot but not on that level. Only where needed in methods or getter.

Basically the use case I have is mixing DI with code-splitting and dependencies may live in a different not-yet-loaded chunk

hbroer commented 2 years ago

I never had a usecase where I needed a async service. I use Promise a lot but not on that level. Only where needed in methods or getter.

Basically the use case I have is mixing DI with code-splitting and dependencies may live in a different not-yet-loaded chunk

This should work without making the service a promise. I had a project in the past wich used async-router (with Preact). The key was to load a seperate bootstrap for that module which binds and loads the service. The project also had some global services which where loaded on the global bootstrap.

I think about this way: The view can only be used if everything is ready, all services are loaded and the view is ready to receive data. This is one chunk to load and the fastest part of the delivered data to the user (static asset cache of the webserver or even the browser cache), the then to load dynamic data is what takes a while and you can display meanwhile animated placeholders within the layout of that module. You can make it more complicated but IMO this is the fastest, still very userfriendly and most efficient way. You don't need promises everywhere, just in key places.