4

I have some components which require some asynchronous calls to be finished before those components are fully initialized and ready for work.

I do not want dependant objects to know about these lifetime issues of their dependencies.

How do you manage that?

8
  • How does your language deal with asynchronous things generally? E.g. the container could expose a future/promise of when it's finished resolving all the dependencies, then it can resolve those individual async initialisations as part of that.
    – jonrsharpe
    Jan 29, 2021 at 8:27
  • @jonrsharpe I am talking about C#, async/await. In fact I am working on a Unity3D game and we use Zenject for DI. Jan 29, 2021 at 8:43
  • 1
    You can inject a proxy instead. The dependant always gets something injected no matter the state of the element being wrapped.
    – Laiv
    Jan 29, 2021 at 10:07
  • @Laiv From my point of view, that's the right answer for all the use cases I can imagine right now. You should post it as an answer. The proxy can also be used to accommodate any logic regarding the uninitialised state: Either block the caller until the dependency is finished with its async task or throw an exception or delegate to some other temporary mock depenceny or something else entirely Jan 29, 2021 at 11:37
  • 1
    You are right. You need a proxy then. The proxy behaves like IBar from the caller standpoint, but also as a facade to hide the async initialization. Making Ibar somewhat lazy initializable too.
    – Laiv
    Jan 29, 2021 at 13:21

4 Answers 4

1

Don't create the dependent objects until the dependency's initialization finishes.

Then there is no such problem. The dependent object always sees the dependency as initialized.

Of course, now you can't create the whole object graph synchronously.

1

Design your own dependencies. Initialize them fully in the constructor. If ever you get stuck with only part of what you need then create a class that only takes that part. Shove it in something else when you get the rest.

Do it this way and everything that exists is ready to be used. Yes it forces you to think up more names for more classes but you don’t have to check if things are ready. If it exists, it’s ready.

0

Take a look at builder pattern and try to employ that. Basically, you do not want to have the dependent classes to worry about dependencies being initialized. Rather, put that logic in the builder class and let Builder class worry about that.

2
  • I know what is the Builder pattern but I cannot see how you suggest to use it. You suggest dependent classes to build dependencies using builders for themselves? Usually in IoC environment we use factories to create something... Jan 29, 2021 at 8:46
  • Ah, I see. You mean how IHostBuilder works... Then consider if your service A depends on another service B and wants to use it but service B did not finished its initialization yet. Looks like service A must be careful using B and I am trying to avoid this. Otherwise your code gets bloated with 'if's checking that this ir that is ready. Jan 29, 2021 at 9:08
0

Wrap this nasty implementation detail in a class so the caller doesn't have to worry about it.

Within the class, store the Task that was returned when you called the initialization routine, then await it before allowing the caller to use any data.

class SomeLibrary
{
    private readonly Task _initializationTask;
    private string _data = null;

    public SomeLibrary()
    {
        _initializationTask = InitializeAsync();
    }

    private async Task InitializeAsync()
    {
        _data = await GetDataAsync(); //Or whatever
    }

    public async Task<string> GetData()
    {
        await _initializationTask;
        return _data;
    }
}
7
  • This will work but I do not like here that this design forces a dependant class to be async/awaited too and this will propagate further down the call chain. Jan 29, 2021 at 9:27
  • If the caller isn't async, what do you want the caller to do if the initialization isn't complete yet? It either has to wait for the initialization to complete or throw an exception. Do you want it to block the thread?
    – John Wu
    Jan 29, 2021 at 9:34
  • So this is the root of my question I guess )) Jan 29, 2021 at 9:38
  • @AntonPetrov: It makes no sense to not want to use await, while at the same time trying to await a task. It also makes no sense to both want this logic not in the actual service, and at the same time not want an additional dependent class to contain the logic.
    – Flater
    Jan 29, 2021 at 9:42
  • @Flater I am not "trying to await a task" :) I want to receive a dependency which is ready for work from the moment I received it. But this dependency may require some async initialization. So I cannot just drop everything into a single container. I need some sort of orchestration and I asked about what others do in that case. Jan 29, 2021 at 9:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.