Skip to content

What's the purpose of dependsOn? #391

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

Closed
JCKodel opened this issue Nov 14, 2024 · 11 comments
Closed

What's the purpose of dependsOn? #391

JCKodel opened this issue Nov 14, 2024 · 11 comments

Comments

@JCKodel
Copy link

JCKodel commented Nov 14, 2024

import 'package:get_it/get_it.dart';

abstract interface class IDep1 {
  void doSomething();
}

abstract interface class IDep2 {
  void doSomething();
}

final class Dep1 implements IDep1 {
  const Dep1(this.dep2);

  final IDep2 dep2;

  @override
  void doSomething() {
    print("Dep1 doing something");
    dep2.doSomething();
  }
}

final class Dep2 implements IDep2 {
  const Dep2();

  @override
  void doSomething() {
    print("Dep2 doing something");
  }
}

Future<void> main() async {
  GetIt.I.registerSingletonWithDependencies<IDep1>(
    () => Dep1(GetIt.I<IDep2>()),
    dependsOn: [IDep2],
  );

  GetIt.I.registerSingleton<IDep2>(const Dep2());

  await GetIt.I.allReady();

  final dep1 = GetIt.I<IDep1>();

  dep1.doSomething();
}

Intuitively, this code should work, because IDep1 is deferred (as it is a factory, not an instance), so, since it is called after all the registrations, why the factory is not called only after allReady or when dependsOn types are available?

How registerSingletonWithDependencies is different from registerSingleton?

@escamoteur
Copy link
Collaborator

escamoteur commented Nov 14, 2024 via email

@JCKodel
Copy link
Author

JCKodel commented Nov 14, 2024

The problem is: I'm not talking about initialization at all! I'm talking about one dependency depending on another =)

All my dependencies does not require initialization. But I have some dependencies that require other dependencies to be ready (and the exception is thrown on registration, which doesn't make any sense).

In my brain, when I see registerSingletonWithDependencies(factory, dependsOn), I understand: ok, this will run the factory when all types in dependsOn are registered.

Not initilized! Registered.

The only way I can achieve that is using registerLazySingleton, which will defer the factory until someone actually need that dependency.

Then, again, what's the purpose of dependsOn??? If registerSingletonWithDependencies behave like registerSingleton, running the factory right in the registration, what's the point?

In my case:

@override
Widget build(BuildContext context) {
  return DependencyContext(
    settings: {
      "FirebaseOptions": DefaultFirebaseOptions.currentPlatform,
      "DefaultThemeSettings": const ThemeSettings(
        seedColor: Colors.pink,
        themeMode: ThemeMode.system,
        fontFamily: "Poppins",
      ),
    },
    dependenciesBuilder: (g) {
      FirebaseAppProvider.register(g);
      FirebaseAnalyticsProvider.register(g);
      AnalyticsService.register(g);
      DeviceInfoProvider.register(g);
      FirebaseAuthProvider.register(g);
      NativeHttpProvider.register(g);
      Database.register(g);
      AuthRepositoryProvider.register(g);
      AuthService.register(g);
      ThemeService.register(g);
    },
    scopeName: "Auth",
    builder: (context) => const _MainApp(),
  );
}

FirebaseAppProvider should be the first thing instantiated, because it doesn't depend on anyone.

FirebaseAnalyticsProvider and FirebaseAuthProvider should be next, because they depend on FirebaseAppProvider.

AnalyticsService uses FirebaseAnalyticsProvider, so it comes next.

Almost anyone from now on uses AnalyticsService.

Since AuthService uses AnalyticsService, DeviceInfoProvider, FirebaseAuthProvider, Database, NativeHttpProvider and AuthRepositoryProvider, it should run after all those are registered.

Finally, ThemeService depends on AuthService, so it would be the last one registered (i.e.: instantiated in the registration factory).

None of those requires initialization whatsoever.

I just thought dependsOn would allow me to register dependencies in any order and GetIt would create the singletons based on whomever they depend on =\

What I did was to actually use registerLazySingleton to defer all instantiations to a time when GetIt actually has all types registered.

static void register(GetIt getIt) {
  getIt.registerLazySingleton<AnalyticsService>(
    () => AnalyticsService._(getIt.get<IAnalyticsProvider>()),
  );
}

Maybe my mistake was to believe that GetIt.I.allReady() would be some kind of internal builder that will make and lock the dependency tree.

For me, dependsOn still doesn't make any sense =\

@escamoteur
Copy link
Collaborator

escamoteur commented Nov 14, 2024 via email

@JCKodel
Copy link
Author

JCKodel commented Nov 14, 2024

easily managed by registering them in the correct sequence

I expected GetIt to handle it for me automagically through registerSingletonWithDependencies. I didn't realize that is actually registerSingletonWithDependeciesThatNeedInitializationBeforeTheyCanBeUsedSyncInThisCase. The function name was a bit misleading for me ;-)

Thanks for the info.

@escamoteur
Copy link
Collaborator

escamoteur commented Nov 14, 2024 via email

@JCKodel
Copy link
Author

JCKodel commented Nov 15, 2024

There is something really wrong with this. I'm getting an infinite getAllReady().

What I'm doing is registering all dependencies as registerSingletonAsync because I can't mix async vs not async (an exception is thrown).

Without that change, one async initializer is never ready (even getAllReady() is called).

With all registration as registerSingletonAsync, getAllReady never completes, because the FutureGroup.close is in this state:

_pending = 1

_values is

image

So, if I use the "correct" registration process, I can't use GetIt.I<Dep>() inside constructors (because they are not registered yet). Mixing async with non async throws exceptions. If I use all of them as registerSingletonAsync, GetIt.I.allReady() never completes =\

I'm not trying to do anything fancy, only the basic of DI: inject dependencies inside other dependencies.

@escamoteur
Copy link
Collaborator

escamoteur commented Nov 15, 2024 via email

@escamoteur
Copy link
Collaborator

ping?

@JCKodel
Copy link
Author

JCKodel commented Nov 17, 2024

I'm not using get_it anymore. Sorry.

@JCKodel JCKodel closed this as completed Nov 17, 2024
@escamoteur
Copy link
Collaborator

escamoteur commented Nov 17, 2024 via email

@JCKodel
Copy link
Author

JCKodel commented Nov 17, 2024

Not meant to be rude. Sorry. I just don't have the code anymore.

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

2 participants