fx.Options and fx.Module behave differently
djmitche opened this issue · 4 comments
Describe the bug
It appears that fx.Invoke
's that are in an fx.Module
do not run until after all fx.Invoke
's outside the module.
This is not documented in fx.Module
, and fx.Invoke
's docs say
Invoke registers functions that are executed eagerly on application start. Arguments for these invocations are built using the constructors registered by Provide. Passing multiple Invoke options appends the new invocations to the application's existing list.
Unlike constructors, invocations are always executed, and they're always run in order. Invocations may have any number of returned values. If the final returned object is an error, it indicates whether the operation was successful. All other returned values are discarded.
emphasis on in order :)
To Reproduce
https://go.dev/play/p/SP93TiMiji9
package main
import (
"context"
"fmt"
"go.uber.org/fx"
)
func main() {
app := fx.New(
fx.Invoke(func() { fmt.Printf("Outer invoke 1\n") }),
fx.Module(
"mod",
fx.Invoke(func() { fmt.Printf("Module invoke\n") }),
),
fx.Invoke(func() { fmt.Printf("Outer invoke 2\n") }),
)
app.Start(context.Background())
app.Stop(context.Background())
}
)
Actual behavior
Outer invoke 1
Outer invoke 2
Module invoke
Expected behavior
Outer invoke 1
Module invoke
Outer invoke 2
Additional context
Changing fx.Module
to fx.Options
(and removing the "mod") makes this behave as expected.
In the use-case I'm working on, I want to write
app := fx.New(
someModule,
anotherModule,
fx.Invoke(func(lc fx.Lifecycle) {
lc.Append(fx.Hook{OnStart: ...})
}),
)
and know that, because it's the last invocation, that fx.Hook is the last thing started in the lifecycle. As it is, everything in the modules is starting after my top-level hook.
This is a valid issue, thanks for reporting this.
Tracking internal issue: GO-1591.
BTW, just to clarify what the issue is - the issue isn't that fx.Options and fx.Module behaves differently. They do behave differently because they're meant for doing different things :)
There are two issues that this issue is tracking:
- Lack of documentation on the invoke order when fx.Module is involved.
- fx.Module invokes are run after the parent-level invokes.