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

bug: service instantiated eagerly when it has a lifecycle hook #14552

Closed
wardbell opened this issue Feb 17, 2017 · 4 comments · Fixed by #15070
Closed

bug: service instantiated eagerly when it has a lifecycle hook #14552

wardbell opened this issue Feb 17, 2017 · 4 comments · Fixed by #15070

Comments

@wardbell
Copy link
Contributor

I'm submitting a ... (check one with "x")

[x] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

When a provided service implements the ngOnDestroy lifecycle hook (the only LC hook honored by Angular at this point), Angular instantiates the service eagerly with the module/component/directive that creates it. But if the service does not implement this hook, Angular waits and only instantiates it when the service is requested.

This is clearly intended behavior. There is a test for it here:

At a minimum we need to document this extraordinary result.

p.s. We also need to add documentation about ngOnDestroy in the regular docs; that is already on the docs' todo list.

More importantly, I believe this behavior is mistaken on several grounds.

  1. It is inconsistent. Why create in the presence of a destroy method and but not create w/o a destroy method?

  2. Such a service could unintentionally acquire resources and start working when it isn't actually needed at all. Developers don't know about this behavior and won't know that their constructors are called before they are expected to be called.

  3. Adding the destroy hook changes the behavior of the service. A service that was once constructed lazily will now be created eagerly for no apparent reason.

  4. It complicates the service API. There is no easy way to make the service wait until needed before it starts doing potentially expensive initialization work. Folks will have to come up with private protocols to call init on the service or bury the init logic in the methods of the service's public API.

  5. It's not clear that its scope of application is as intended. The behavior appears to have been introduced in connection with NgModules but, per the test and my experience, it applies also when the service is provided to components and directives.

This "feature" appears to have been in the product since August of 2016 (ecdaded) so I can't say that it is a breaking change.

Expected behavior

Please either remove this behavior for Angular v.4 OR explain what good purpose it serves. In the latter case, the documentation should convey this explanation to users so they will understand what is otherwise a mysterious and inconsistent result.

  • Angular version: 2.4.X (since RC5)

It's still in master.

@DzmitryShylovich
Copy link
Contributor

DzmitryShylovich commented Feb 17, 2017

remove this behavior for Angular

Eager/lazy initialization is a must for any decent DI impl.

But I agree it was a poor design decision to use ngOnDestroy for this. There's already a proposal (#13960) to change configuration by:

  1. extending provider syntax
{provide: Service, useClass: MyService, lazy: false}
  1. extending Injectable annotation
@Injectable({lazy: false})

So I would say it's a duplicate of #13960

@wardbell
Copy link
Contributor Author

a must for any decent DI impl

Really? Where is that written? I do not know of any DI system that instantiates its services until they are requested. I've been using DI for 15 years.

Why would it be OK to wait when there is no ngOnDestroy. If decency demands immediate construction, then that should have been true for all dependencies, whether or not they had a particular method.

What is the value of constructing a service immediately, before it is ever requested? What purpose does that serve? I can't think of a use case. And if I did (and it would be rare), I could achieve the intended effect trivially by immediately injecting the service in the module/component/directive that provided it.

Whether decent or indecent, I'm still waiting for an explanation.

@DzmitryShylovich
Copy link
Contributor

I do not know of any DI system that instantiates its services until they are requested. I've been using DI for 15 years.

Spring. the most popular java framework.

Google Guice also has the ability to eager/lazy initialize services.

If you don't have such a use case it doesn't mean nobody hasn't.

tbosch added a commit to tbosch/angular that referenced this issue Mar 10, 2017
Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly. Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

Fixes angular#14552
tbosch added a commit to tbosch/angular that referenced this issue Mar 10, 2017
Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly. Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

This also makes all `useValue` providers eager, which
should have not observable impact other than code size.

Fixes angular#14552
tbosch added a commit to tbosch/angular that referenced this issue Mar 10, 2017
Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly. Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

This also makes all `useValue` providers eager, which
should have not observable impact other than code size.

Fixes angular#14552
tbosch added a commit to tbosch/angular that referenced this issue Mar 10, 2017
BREAKING CHANGE:

Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly.

Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

This also makes all `useValue` providers eager, which
should have no observable impact other than code size.

EXPECTED IMPACT:
Making providers eager was an incorrect behavior and never documented.
Also, providers that are used by a directive / pipe / ngModule stay eager.
So the impact should be rather small.

Fixes angular#14552
tbosch added a commit to tbosch/angular that referenced this issue Mar 10, 2017
BREAKING CHANGE:

Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly.

Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

This also makes all `useValue` providers eager, which
should have no observable impact other than code size.

EXPECTED IMPACT:
Making providers eager was an incorrect behavior and never documented.
Also, providers that are used by a directive / pipe / ngModule stay eager.
So the impact should be rather small.

Fixes angular#14552
tbosch added a commit to tbosch/angular that referenced this issue Mar 13, 2017
BREAKING CHANGE:

Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly.

Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

This also makes all `useValue` providers eager, which
should have no observable impact other than code size.

EXPECTED IMPACT:
Making providers eager was an incorrect behavior and never documented.
Also, providers that are used by a directive / pipe / ngModule stay eager.
So the impact should be rather small.

Fixes angular#14552
tbosch added a commit to tbosch/angular that referenced this issue Mar 13, 2017
BREAKING CHANGE:

Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly.

Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

This also makes all `useValue` providers eager, which
should have no observable impact other than code size.

EXPECTED IMPACT:
Making providers eager was an incorrect behavior and never documented.
Also, providers that are used by a directive / pipe / ngModule stay eager.
So the impact should be rather small.

Fixes angular#14552
tbosch added a commit to tbosch/angular that referenced this issue Mar 13, 2017
BREAKING CHANGE:

Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly.

Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

This also makes all `useValue` providers eager, which
should have no observable impact other than code size.

EXPECTED IMPACT:
Making providers eager was an incorrect behavior and never documented.
Also, providers that are used by a directive / pipe / ngModule stay eager.
So the impact should be rather small.

Fixes angular#14552
chuckjaz pushed a commit that referenced this issue Mar 14, 2017
BREAKING CHANGE:

Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly.

Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

This also makes all `useValue` providers eager, which
should have no observable impact other than code size.

EXPECTED IMPACT:
Making providers eager was an incorrect behavior and never documented.
Also, providers that are used by a directive / pipe / ngModule stay eager.
So the impact should be rather small.

Fixes #14552
SamVerschueren pushed a commit to SamVerschueren/angular that referenced this issue Mar 18, 2017
…5070)

BREAKING CHANGE:

Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly.

Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

This also makes all `useValue` providers eager, which
should have no observable impact other than code size.

EXPECTED IMPACT:
Making providers eager was an incorrect behavior and never documented.
Also, providers that are used by a directive / pipe / ngModule stay eager.
So the impact should be rather small.

Fixes angular#14552
smurfy pushed a commit to smurfy/angular that referenced this issue Apr 7, 2017
…5070)

BREAKING CHANGE:

Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly.

Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

This also makes all `useValue` providers eager, which
should have no observable impact other than code size.

EXPECTED IMPACT:
Making providers eager was an incorrect behavior and never documented.
Also, providers that are used by a directive / pipe / ngModule stay eager.
So the impact should be rather small.

Fixes angular#14552
asnowwolf pushed a commit to asnowwolf/angular that referenced this issue Aug 11, 2017
…5070)

BREAKING CHANGE:

Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly.

Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

This also makes all `useValue` providers eager, which
should have no observable impact other than code size.

EXPECTED IMPACT:
Making providers eager was an incorrect behavior and never documented.
Also, providers that are used by a directive / pipe / ngModule stay eager.
So the impact should be rather small.

Fixes angular#14552
juleskremer pushed a commit to juleskremer/angular that referenced this issue Aug 28, 2017
…5070)

BREAKING CHANGE:

Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly.

Now, only classes that are annotated with @component, @directive, @pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.

This also makes all `useValue` providers eager, which
should have no observable impact other than code size.

EXPECTED IMPACT:
Making providers eager was an incorrect behavior and never documented.
Also, providers that are used by a directive / pipe / ngModule stay eager.
So the impact should be rather small.

Fixes angular#14552
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 11, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants