Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Why is the call to $apply needed in $http? #13108

Closed
thorn0 opened this issue Oct 16, 2015 · 12 comments
Closed

Why is the call to $apply needed in $http? #13108

thorn0 opened this issue Oct 16, 2015 · 12 comments

Comments

@thorn0
Copy link
Contributor

thorn0 commented Oct 16, 2015

I started to think about it looking at #12557. At first glance the answer is obvious: we need to update the views after we got a response, so we trigger a digest. But hey, it'll be triggered anyway because $http uses $q internally. And when a $q deferred is resolved, it schedules a digest. So can't we just remove that call to $rootScope.$apply (with all the $applyAsync thing altogether)? Am I missing something? If it's needed only for the tests, the $apply call should be moved to $httpBackend.flush.

@gkalpak
Copy link
Member

gkalpak commented Oct 16, 2015

Hey, this sounds about right. I would try replacing $q with $$q inside $httpand keep $http's apply (sync or async) - probably disable-able with an extra argument.

(This has been discussed before (more or less). See #2049 and #2049 (comment).)

@thorn0, would fancy giving it a shot ?

@thorn0
Copy link
Contributor Author

thorn0 commented Oct 16, 2015

I would try replacing $q with $$q inside $http and keep $http's apply (sync or async)

It won't work this way. The $apply call in $http is useless (or seems so if I'm still missing something) because it happens just after the response comes, but before any callbacks are called, which means before any scopes are modified. So what changes is it supposed to digest? After git blame'ing, it seems to me that this $apply call is merely a historical rudiment.

And please note that it's not the same issue as #2049 and #12557. It's more of a small performance refactoring. $applyAsync isn't needed. We'll get the same effect if we just remove this $apply call.

@gkalpak
Copy link
Member

gkalpak commented Oct 16, 2015

True. Yet, by merely removing $http's apply, we can't have the applyAsync perf improvement (which we don't have now either I think).

So, I would still try replacing $$q with $q and add another callback at the end of the promise chain to make the digest (sync or async). That way we can easily have normal apply (using $rootScope.$degest()), async apply (using $rootScope.$applyAsync()) or add an extra option to skip apply altogether (in which case we should not add anything at the end of the chain).

Again, this is off the top of my head, so take it with a grain of salt 😃

@gkalpak
Copy link
Member

gkalpak commented Oct 16, 2015

Or do you mean removing the $rootScope.$apply(), but keep the $rootScope.$applyAsync(...) ?
That would work as well, I guess, but we would still get an extra $digest in the async case (I think).

Plus, replacing $q with $$q would also enable skipping apply altogether (as an option).

@thorn0
Copy link
Contributor Author

thorn0 commented Oct 16, 2015

try replacing $$q with $q and add another callback at the end of the promise chain to make the digest

We can break someone's response interceptors this way.

Or do you mean removing the $rootScope.$apply(), but keep the $rootScope.$applyAsync(...) ?

No, I mean we can safely remove all these lines:

        if (useApplyAsync) {
          $rootScope.$applyAsync(resolveHttpPromise);
        } else {
          resolveHttpPromise();
          if (!$rootScope.$$phase) $rootScope.$apply();
        }

and leave only

        resolveHttpPromise();

@gkalpak
Copy link
Member

gkalpak commented Oct 16, 2015

try replacing $$q with $q and add another callback at the end of the promise chain to make the digest

We can break someone's response interceptors this way.

How exactly ?

Or do you mean removing the $rootScope.$apply(), but keep the $rootScope.$applyAsync(...) ?

No, I mean we can [...] leave only resolveHttpPromise();.

That would essentially remove the useApplySync optimization.
We don't want to remove that one (I suppose it was added for a reason).

@thorn0
Copy link
Contributor Author

thorn0 commented Oct 16, 2015

try replacing $$q with $q and add another callback at the end of the promise chain to make the digest

We can break someone's response interceptors this way.

How exactly ?

Those interceptors can modify the scopes (or other $watched values) and expect a digest to happen earlier than in the end of the interceptor chain.

No, I mean we can [...] leave only resolveHttpPromise();

That would essentially remove the useApplySync optimization.
We don't want to remove that one (I suppose it was added for a reason).

No, we'll just get the same effect for free.

@gkalpak
Copy link
Member

gkalpak commented Oct 16, 2015

OK, I'll believe you when I see it 😛

thorn0 added a commit to thorn0/angular.js that referenced this issue Oct 16, 2015
@thorn0
Copy link
Contributor Author

thorn0 commented Oct 16, 2015

OK, please see this PR #13111.

@thorn0
Copy link
Contributor Author

thorn0 commented Oct 19, 2015

I think this issue is important in the first place because $http should be a model for building async services. It should be a canonical example of how async operations and the digest cycle should be married so that libraries like bramski/angular-indexedDB could learn from it. But unfortunately, this example is misleading now. It leaves people wonder why it was written the way it was, why this $apply call can be needed.

@rcollette
Copy link

rcollette commented Feb 17, 2017

Is there a consideration that needs to be made for JavaScript to Native calls with regards to digest cycles?
http://stackoverflow.com/questions/42305044/angularjs-digest-already-in-progress-when-calling-javascript-native-bridge-func

thorn0 added a commit to thorn0/angular.js that referenced this issue Feb 7, 2018
This `$apply` call was needed only for tests, so it's been moved to `$httpBackend.flush`.

BREAKING CHANGE: `$httpProvider.useApplyAsync` has been removed.

Closes angular#13108
Closes angular#13111
@jbedard jbedard self-assigned this May 3, 2018
@jbedard
Copy link
Contributor

jbedard commented May 9, 2018

Closing, see: #13128 (comment)

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

No branches or pull requests

5 participants