Skip to content
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

Support for Async Await #538

Open
mcgaryp opened this issue Sep 23, 2023 · 9 comments
Open

Support for Async Await #538

mcgaryp opened this issue Sep 23, 2023 · 9 comments

Comments

@mcgaryp
Copy link

mcgaryp commented Sep 23, 2023

Would love some support with object that Async initializers as injected dependencies or just objects that are themselves async initialized.

container.register { resolver in
    let asyncInitObject = await resolver.asyncResolve(AsyncInitObject.self)
    return MyObject(asyncInitObject
}
container.register { resolver in
    let someObject = resolver.asyncResolve(SomeObject.self)
    return await MyAsyncInitObject(someObject)
}
let resolver: Resolver
let object = await resolver.resolve(MyAsyncInitObject.self)
@chosa91
Copy link

chosa91 commented Sep 25, 2023

Just asking: what is the use case where it is justified to have an init async? 🤔
It sounds to me like the initialization has been combined with "setup"/"start"/..., which shouldn't be part of the init phase

@serges147
Copy link

serges147 commented Sep 28, 2023

Just asking: what is the use case where it is justified to have an init async? 🤔 It sounds to me like the initialization has been combined with "setup"/"start"/..., which shouldn't be part of the init phase

Easy - f.e. in .init you work with an external entity which is an actor by itself (or has internally an actor, so its api is async as well). And such work in the .init does not necessarily a long asynchronous work, it could be just matter of data protection an actor provides in concurrent environment. Also, moving from .init to a "setup"/"start"/... might mean that you have to make some essentially constant stuff as var (instead of let) member, which in turn has various other drawbacks (including the very same concurrency worm-can opened).

@chosa91
Copy link

chosa91 commented Sep 28, 2023

I just modeled the above use-case and it seems to me that the actor initialization shouldn't be awaited , as it is not async. Could you please show us an example where this is a problem?

actor Bar {
    let baz: Int

    init(baz: Int) {
        self.baz = baz
    }
}

class Foo {
    let bar: Bar

    init(bar: Bar) {
        self.bar = bar
    }
}


let bar = Bar(baz: 42)
let foo = Foo(bar: bar)

I'm not saying it has no place in Swinject, I just don't see the use-case clearly yet, so please help me if you have time

@serges147
Copy link

This is not what I meant. Try this to make happen:

actor Bar { var counter = 0; func increment() { counter += 1 } }
class Foo { init(_ bar: Bar) { bar.increment() } }

let bar = Bar()
let foo = resolver.resolve(Foo.self, argument: bar)

..., and you can't ! B/c the only way to call bar.increment() is as await bar.increment(), hence Foo.init has to be be async.

@serges147
Copy link

But at the same time I anticipate that it won't be just as easy as adding a new func asyncResolve(...). I believe you can't fit async stuff into current completely synchronous Container/Resolver infrastructure. IHMO, instead there should be parallel "universe" of async versions of these two, like: ContainerAsync and ResolverAsync. Implementation will not contain any locks, mutexes and etc, but instead use actors to protect registrations and later resolvings, supporting of course async factory closures (and thus async .init-s) - all (including initial registrations!) completely thread-safe and ready to be used in concurrent code.

@mcgaryp
Copy link
Author

mcgaryp commented Sep 28, 2023

But at the same time I anticipate that it won't be just as easy as adding a new func asyncResolve(...). I believe you can't fit async stuff into current completely synchronous Container/Resolver infrastructure. IHMO, instead there should be parallel "universe" of async versions of these two, like: ContainerAsync and ResolverAsync. Implementation will not contain any locks, mutexes and etc, but instead use actors to protect registrations and later resolvings, supporting of course async factory closures (and thus async .init-s) - all (including initial registrations!) completely thread-safe and ready to be used in concurrent code.

Your thought here is very similar to my initial thoughts. It would be little complicated to do as is by just adding an asyncResolver(), although not necessarily impossible.

It would be nice if the "Async Universe" was also backwards compatible "Non-Async Universe" which would limit the need to completely migrate for just async functionality.

@mcgaryp
Copy link
Author

mcgaryp commented Sep 28, 2023

At the moment I don't have a lot of spare time to work out a POC but should after the new year, if it's not already done by then 😁

@ynnckcmprnl
Copy link

Any news regarding the stance on async/await in combination with Swinject?

@alessdiimperio
Copy link

One thought perhaps could be along the lines of @mainactor conformance.

If i want to use a dataStore and want the whole class to be wrapped in @mainactor so that updates have to be done on the mainThread i cannot initiate the class unless the init is also wrapped in @mainactor, and this propagates all the way to the assemble function having to be annotated with @mainactor but seeing as the protocol doesn't have @mainactor it will lose it's actor safety

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants