Sometimes i need to use $scope.$apply, for example when i use jQuery ajax or some non-angular-js eventlisteners. In those cases i have an asynchronous callback and there i use $scope.$apply so that changes to the scope are picked up by angular and watches are triggered. Please correct me if that is the wrong use of $scope.$apply.
This seems to work and the view is updated. However in some rather rare cases I get a "digest already in progress" error. I don't see how this can be possible, since the callback is not synchronous. So I wonder if it is possible that my asynchronous callback with the $scope.$apply in it can by chance collide with an ongoing digest? If so, how can I prevent this from happening?
edit:
One possibility to check for a digest is checking $$phase: if (!$scope.$$phase) $scope.$apply() but this is an anti-pattern as the angularjs wiki says: https://github.com/angular/angular.js/wiki/Anti-Patterns
I want to fully understand why it is possible that I encounter a digest in an asynchronous callback. And why this is an antipattern.
I recently asked a similiar question on why if (!$scope.$$phase) $scope.$apply() is an anti-pattern here: Why is using if(!$scope.$$phase) $scope.$apply() an anti-pattern?
The answer there also contains the answer to this question.
You are calling $apply on an existing scope, so it's definitely possible that you are calling apply while it is currently digesting. Some people might suggest checking $$phase but that has been discouraged as an anti-pattern
You have two options if you are running into this problem, even occasionally. One is to do as the anti-pattern implies and make sure your $scope.$apply is as high as possible up the chain.
However, this won't help if you are asynchronously calling $apply on the same $scope in rapid succession. The answer then is often to throttle the $apply so that it only happens every few hundred milliseconds. This is a common practice when using something like socket.io that can fire events which could cause you to re-apply many times.
I think, that its normal, but it will be better to move code to services and resources with angular style. To prevent error you can check current state
scope.$$phase
To prevent calling $apply
Async Callbacks are an appropriate time to use the $apply, there are edgecases I have run into as well where i get that error. They are usually because I am doing something wonky, I have found that a "Conditional Apply" works well for these.
if(!$scope.$$phase) {
//$digest or $apply
}
AngularJS : Prevent error $digest already in progress when calling $scope.$apply()
Related
For ease in explaining the significance of 'then', could anyone tell me what is happening in this code?
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
fetchComments returns a promise (probably; it could just be a "thenable"*). A promise is something that will be either resolved or rejected at a later point in time (typically**). then is used to hook up a handler that will be called when the promise is resolved (and optionally when it's rejected, if you pass a second function into then; otherwise you'd use catch).
In this case, that code says that when/if the promise returned by fetchComments resolves, use the resolution value to set the state of the React component using the comments property of that resolution value.
More about promises in this MDN article and in the Promises/A+ spec.
* See the Promises/A+ spec for what a "thenable" is.
** If you use then on a promise that's already resolved or rejected, you're guaranteed by the native promises in JavaScript that your handler will still be called asynchronously. That wasn't always the case with some early promise-like implementations, which would either call your callback asynchronously (if the promise wasn't already settled) or synchronously (if it was), which was...chaotic and unhelpful. JavaScript's native promises and any really good promise library guarantees consistent callback behavior.
I am answering this question bit late but may be helpful to someone at some time.
Let's Start:
From the above code you pasted, I can give you a hint that whenever you see a keyword then in any Javascript code snippet, that is an asynchronous function using promise.
Promise: are objects which store information about whether or not those events have happened yet, and if they have, what their outcome is.Usually, promise will handle success (a.k.a resolve in js code) and failure(a.k.a reject in js code) and also both. So when we create any async functions, the promise is created inside these async functions.
Promise.then: then allows us to assign event handlers to a promise. Depending on the arguments we supply, we can handle success, failure, or both and the return of then is also a promise which means it can handle more events.
And finally to get to the code above, fetchComments is a promise which is an async function, and when the response is resolve it is updating the state of the comments and additionally here we can also handle error scenarios using .catch or even by adding another then
and to end, below link has a nice explanation:
A nice tutorial on Promise and then in javascript is here
Function fetchComments 'll fetch Data and return one Promise then give them to state comments :). But I think you should read here^^.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
While going through the Redux middleware, I have come across Axios which is 'Promise-based HTTP client' to make Ajax request from browser. Could any body explain about this term and some info about axios?
Before Promise's comes into the picture there is something called Callback to handle asynchronous calls/code. But the main issue with callback is that when it gets nested and more nested it becomes very messy code and harder to understand even for the writer of the code.
To make it bit cleaner Promise comes into the picture which is cleaner in nature, Avoid callback hell problem in nodejs programming.
Moreover the concept async/await is also best fit with the promise instead of callback.
Hope you understand now. Comment below in case of any doubt.
From MDN:
A Promise is a proxy for a value not necessarily known when the
promise is created. It allows you to associate handlers with an
asynchronous action's eventual success value or failure reason. This
lets asynchronous methods return values like synchronous methods:
instead of immediately returning the final value, the asynchronous
method returns a promise to supply the value at some point in the
future.
A Promise is in one of these states:
pending: initial state, neither fulfilled nor rejected.
fulfilled: meaning that the operation was completed successfully.
rejected: meaning that the operation failed.
A promise-based client returns promises rather than accepting callbacks.
it("should update code when the key is changed through selector", function () {
var Code = "123";
var expectedCode;
service.updateCode(Code).then(function (expectedCode) {
expectedCode = expectedCode;
});
// promises are resolved/dispatched only on next $digest cycle
$rootScope.$apply();
expect(expectedCode).toEqual(`enter code here`Code);
});
Why we need to call $rootScope.apply() explicitly?
Promises are resolved/rejected during the digest cycle which can be triggered by you by calling $rootScope.$apply() or even $rootScope.$digest.
So basically by calling that you are telling angular to process the handlers for the promises that you've resolved/rejected in order to evaluate the results for testing.
When your application is running the digest loop is evaluated automatically for things that happen with the scope of your angular application, so you do not need to call this directly within your application for most cases.
Because then it would be a lot harder to test. I know this may sound counter-intuitive, but once you get a little bit into promise testing you'll understand what I mean.
It would be a pain to expect() stuff from promises, because you would always run into race-conditions. In your example if you omit the $apply(), due to the async nature of promises, the expect would probably be called before the promise is resolve.
So by manually having to $apply/$digest it basically changes your async stuff into a synchronous.
After executing get or put in indexeddb, the callback is updating the scope.
The problem is, no update is being triggered in the ui!
A common solution is to use apply or digest but that is wrong, you should NOT use those operations. Angular should update it automatically.
Now my guess, after reading some stuff, is that the callback is being executed in a different context, outside of the scope.
My question is basically: how can I execute an indexeddb callback in an angularjs scope context?
EDIT:
A rough look on how its built:
GetObjectStoreData(iDb, objectStore, function (res) {
$scope.result = res;
}
The call is made from inside the scope so the scope is used in the callback. The callback parameter is the contents of the object store
You asserted that you should not have to use Scope.$apply because AngularJS should do it for you.
This is true only for callbacks managed by Angular. For example, when you use $timeout in place of window.setTimeout, angular wraps your provided callback in a call to Scope.$apply, causing a digest cycle to run once your callback completes. If you interact directly with browser APIs rather than calling through AngularJS, it is your responsibility to call Scope.$apply at the appropriate time.
If you don't want to manage the scope directly, you could instead use a wrapper library like angular-indexedDB, which (amongst other things) handles the scope notifications when callbacks occur.
Our clients add our JS tag to the head of their page. Our tag needs to know when reactJS has finished before it modifies the page.
I have tried using jQuery's $(document).ready() but this fires before reactJS has finished. I can use $(window).load() but if there are a lot of images on the page, this would be too slow.
What are the options to bind to the completion of React.renderComponent?
Set off the execution of your code via the optional callback that you can pass to React.renderComponent. This callback won't execute until React.renderComponent has finished. If you don't have access to the code that is executing React.renderComponent (which I've just realized is almost definitely the case) then there is no definitive/reliable way to listen for the execution of a function unless of course that function exposes some sort of event that you can listen for, or even guarantees that "x" will be the case once it has finished. Sadly for your case, this doesn't appear to be an option with React.renderComponent.. Let me know if you have any further question.
If you don't have direct control over the React code, you could always see if you can sniff for relevant changes via DOM Mutation events.