Angularjs - Digest Loop/Repaint timing - javascript

having a small issue with AngularJS. See the following:
$http({method: 'GET', url: 'some/url/withJSON.json'})
.success(function(data)
{
$scope.data = data
$animations._load('arg1')
})
$animations is a registered service that does some stuff. Occasionally (maybe 20%) of the time the _load() method will run before the window has been painted/the digest loop completes, meaning the visualisation hasn't completed. By wrapping _load() with a setTimeout() of 10ms this has resolved it, but is there an implicit/explicit way in AngularJS to execute a callback once the digest loop has finished?

Without more information, I would suggest $timeout(fn) will be the best way to do this. It basically makes sure there's a break after angular completes processing for the browser to render. Roughly it is equivalent to setTimeout(fn, 0), so is what you have already suggested, but it is mockable for testing.
There is also $scope.$evalAsync(fn) that would normally render after the current digest cycle but before the DOM renders. I tend to prefer $evalAsync as it makes things easier for me to reason about, and won't allow other non-angular happenings to occur in the meantime, but it depends which works for you.
This answer has more detailed information about the two methods.

Related

AngularJS and jQuery alert

I'm just curious why the following code is working:
<script>
var app = angular.module('VEL',[]);
app.controller('VELControl', function($scope, $http) {
$.getJSON("../json/dict.php?p="+$_GET['p'], function(response){
$scope.velData = response;
});
alert($scope.velData); // also works with alert($scope);
});
But when I remove the alert(...) it stops working.
Could anyone explain me why or refer to a documentation?
The main problem is that you're using jQuery's $.getJSON() method, which Angular doesn't know anything about. Angular is great at tracking when asynchronous events occur and updating the view when they complete, but you have to use Angular's services to take advantage of this.
Angular gives us the $http service for making AJAX calls. Since $http is tied into Angular's digest cycle (read up on this if you've never heard of it, it's critical to understanding how Angular works), Angular will update the view after $http returns data and its callbacks are run.
Rewrite your controller like this and it should work:
app.controller('VELControl', function($scope, $http) {
$http.get("../json/dict.php?p="+$_GET['p']).then(function(response){
$scope.velData = response.data;
});
});
As an aside, your alert() working with the old method was probably a fluke based on how fast your server returned the data. As I understand it, alert() runs asynchronously anyway, so when you called alert($scope.velData), this was probably pushed onto the end of the browser's execution queue, and before it was reached the AJAX call returned and set the value you needed. If your server had been slower, you probably would have alerted undefined and then received your data. If you have control over the server (which it looks like you do), this would probably be a fun experiment to try: put in a sleep(10) or some similar wait to force the server to take a long time; try different values and see what happens with that alert() statement.

DOM update after async call

I am fetching some data from my server and updating the DOM through Angular two way bindning. It is however not working as expected and I need to wrap it inside an ugly setTimeout function for the DOM to have time to update.
$http.post('myBackend.php', someData)
.then(function(res){
$scope.data = res.data;
doStuffWithDOMElements(); // Does not work
});
while this works:
$http.post('myBackend.php', someData)
.then(function(res){
$scope.someDataToPopulateDOMwith = res.data;
setTimeout(function(){ doStuffWithDOMElements();}, 50); // Yup, works
});
The line that gives error without the timeout "Can't read propertly of null" is this:
let y_0 = document.getElementById("l_0").offsetTop;
and in my index.html
<div id="l_{{$index}}" ng-repeat = "x in data"></div>
This is weird. Shouldn't every DOM element that is wrapped up in Angular "events" be updated automatically? $scope.$apply() does not work and shouldn't be necessary either. What's wrong here?
The need of $timeout comes every once in a while in angularjs.Most probably to init a jQuery plugin.
Your Error line:
let y_0 = document.getElementById("l_0").offsetTop;
is because your DOM has not yet been set and you are trying to get the element which yet has to be set or rather rendered in the DOM.
When you use $timeout,it should run after the DOM has been manipulated by Angular, and after the browser renders (which may cause flicker in some cases).That is why it is working in your case when you set the $timeout.
If you want to learn more about digest cycle. Your should know about $evalAsync as well.
If code is queued using $evalAsync from a directive, it should run after the DOM has been manipulated by Angular, but before the browser renders
If code is queued using $evalAsync from a controller, it should run before the DOM has been manipulated by Angular (and before the browser renders) -- rarely do you want this
If code is queued using $timeout, it should run after the DOM has been manipulated by Angular, and after the browser renders (which may cause flicker in some cases).
FurtherMore, Angularjs is a javascript framework. A browser has to do a number of things pretty much all at once, and just one of those is execute JavaScript. But one of the things JavaScript is very often used for is to ask the browser to build a display element. This is often assumed to be done synchronously (particularly as JavaScript is not executed in parallel) but there is no guarantee this is the case and JavaScript does not have a well-defined mechanism for waiting.
The solution is to "pause" the JavaScript execution to let the rendering threads catch up. And this is the effect that setTimeout() with a timeout of 0 does. It is like a thread/process yield in C. Although it seems to say "run this immediately" it actually gives the browser a chance to finish doing some non-JavaScript things that have been waiting to finish before attending to this new piece of JavaScript.
(In actuality, setTimeout() re-queues the new JavaScript at the end of the execution queue. See the comments for links to a longer explanation.)
IE6 just happens to be more prone to this error, but I have seen it occur on older versions of Mozilla and in Firefox.
Also, a lot has been written and explained about why the use of $timeout comes in handy time to time.
Links where you will find good explanation:
Why is setTimeout(fn, 0) sometimes useful?
AngularJS : $evalAsync vs $timeout
Breakup the Operations
One of the things to do is breakup the operations by chaining them.
$http.post('myBackend.php', someData)
.then (function onFulfilled (response) {
$scope.someDataToPopulateDOMwith = response.data;
return response;
}).then (function onFulfilled2 (response) {
doStuffWithDOMElements();
});
This allows the $q service to execute a digest cycle before invoking the second fulfillment handler. The AngularJS framework needs to do a digest cycle so the watch handlers for the ng-repeat directive have an opportunity update the DOM. The ng-repeat directive needs to finish updating the DOM before the doStuffWithDOMElements function can safely manipulate the DOM.
Use $timeout Service
Avoid using the raw browser setTimeout function, instead use the $timeout service. The AngularJS $q service then automatically does digest cycles.
Since the $timeout service returns promises, it can be used for chaining.
$http.post('myBackend.php', someData)
.then (function onFulfilled (response) {
$scope.someDataToPopulateDOMwith = response.data;
return response;
}).then (function onFulfilled2 (response) {
//return $timeout promise
return $timeout(function(){return response}, 1000);
}).then (function onFulfilled3 (response) {
doStuffWithDOMElements();
});
Because calling the then method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.1
Re-factor the Design
The AngularJS is a MVW* framework.
Avoid manipulating HTML DOM programmatically: Manipulating HTML DOM is a cornerstone of AJAX applications, but it's cumbersome and error-prone. By declaratively describing how the UI should change as your application state changes, you are freed from low-level DOM manipulation tasks. Most applications written with Angular never have to programmatically manipulate the DOM, although you can if you want to.2
Look into modeling what the doStuffWithDOMElements() function does and creating custom directives to watch that model and update the DOM. This fits better with the AngularJS framework and will avoid these timing issues.
in case of DOM manipulation in angular,it is well known.
see this SO link

Forever loop while waiting for asynchronous task?

I'm wondering if there's a way to cause JavaScript to wait for some variable-length code execution to finish before continuing using events and loops. Before answering with using timeouts, callbacks or referencing this as a duplicate, hear me out.
I want to expose a large API to a web worker. I want this API to feel 'native' in the sense that you can access each member using a getter which gets the information from the other thread. My initial idea was to compile the API and rebuild the entire object on the worker. While this works (and was a really fun project), it's slow at startup and cannot show changes made to the API without it being sent to the worker again after modification. Observers would solve part of this, and web workers transferrable objects would solve all, but they aren't adopted widely yet.
Since worker round-trip calls happen in a matter of milliseconds, I think stalling the thread for a few milliseconds may be an alright solution. Of course I would think about terminating in cases where calls take too long, but I'm trying to create a proof of concept first.
Let's say I want to expose the api object to the worker. I would define a getter for self.api which would fetch the first layer of properties. Each property would then be another getter and the process would continue until the final object is found.
worker.js
self.addEventListener('message', function(event) {
self.dataRecieved = true;
self.data = event.data; // would actually build new getters here
});
Object.defineProperty(self, 'api', {
get: function() {
self.dataRecieved = false;
self.postMessage('request api first-layer properties');
while(!self.dataRecieved);
return self.data; // whatever properties were received from host
}
});
For experimentation, we'll do a simple round-trip with no data processing:
index.html (only JS part)
var worker = new Worker("worker.js");
worker.onmessage = function() {
worker.postMessage();
};
If onmessage would interrupt the loop, the script should theoretically work. Then the worker could access objects like window.document.body.style on the fly.
My question really boils down to: is there a way to guarantee that an event will interrupt an executing code block?
From my understanding of events in JavaScript, I thought they did interrupt the current thread. Does it not because it's executing a blank statement over and over? What if I generated code to be executed and kept doing that until the data returned?
is there a way to guarantee that an event will interrupt an executing code block
As #slebetman suggests in comments, no, not in Javascript running in a browser's web-worker (with one possible exception that I can think of, see suggestion 3. below).
My suggestions, in decreasing order of preference:
Give up the desire to feel "native" (or maybe "local" might be a better term). Something like the infinite while loop that you suggest also seems to be very much fighting agains the cooperative multitasking environment offered by Javascript, including when thinking about a single web worker.
Communication between workers in Javascript is asynchronous. Perhaps it can fail, take longer than just a few milliseconds. I'm not sure what your use case is, but my feeling is that when the project grows, you might want to use those milliseconds for something else.
You could change your defined property to return a promise, and then the caller would do a .then on the response to retrieve the value, just like any other asynchronous API.
Angular Protractor/Webdriver has an API that uses a control flow to simulate a synchronous environment using promises, by always passing promises about. Taking the code from https://stackoverflow.com/a/22697369/1319998
browser.get(url);
var title = browser.getTitle();
expect(title).toEqual('My Title');
By my understanding, each line above adds a promise to the control flow to execute asynchronously. title isn't actually the title, but a promise that resolves to the title for example. While it looks like synchronous code, the getting and testing all happens asynchronously later.
You could implement something similar in the web worker. However, I do wonder whether it will be worth the effort. There would be a lot of code to do this, and I can't help feeling that the main consequence would be that it would end up harder to write code using this, and not easier, as there would be a lot of hidden behaviour.
The only thing that I know of that can be made synchronous in Javascript, is XMLHttpRequest when setting the async parameter to false https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#Parameters. I wonder if you could come up with some sort of way to request to the server that maintains a connection with the main thread and pass data along that way. I have to say, my instinct is that this is quite an awful idea, and would be much slower than just requesting data from the main thread.
For what I know, there is not something native in JS to do this but it is relatively easy to do something similar. I made one some time ago for myself: https://github.com/xpy/whener/blob/master/whener.js .
You use it like when( condition, callback ) where condition is a function that should return true when your condition is met, and callback is the function that you want to execute at that time.

How often does the AngularJS digest loop run?

When discussing the merits of AngularJS, two-way data binding is often touted as a major benefit of Angular over other JS frameworks. Digging deeper, the documentation suggests this process is done through dirty-checking rather than through event-driven measures. At first, it seems that the digest-loop works by having a method fire off in the background at periodic intervals, checking all the $watches during each cycle. However, reading further, it seems that the digest-loop is actually triggered by rootScope.digest(), which in turn is triggered by $.apply, which is in turn triggered by an event(!), such as an onClick event called through ng-click.
But, how can this be? I thought Angular doesn't use change listeners. So how does the digest-loop really operate? Does Angular automatically kick-off the digest-loop internally, or is the digest-loop triggered by events? If the digest-loop is run automatically, how often does it run?
Some clarification points:
I'm not asking about how the digest-loop runs when manually binding to changes. In this case, if you want to force a digest loop, you can do so by calling $.apply()
I'm also not asking about how often digest loop runs in response to user events. For example, if ng-model is on an input box, Angular will kick-off a digest loop when a user starts typing. The confusing part is that in order to know a user was typing, doesn't Angular use an event-based onKeyUp somewhere?
I already know that there is a limit of 10 cycles max per digest-loop. My question is less about the number of cycles per digest-loop, but rather the number of digest-loops that run, say, per second.
Bonus questions: How does the digest-loop relate to the JavaScript event-loop? Does the JS event loop run periodically in the background? Is the digest-loop the same thing as the event loop, but only in the "Angular Context"? Are these totally different ideas?
Angular digests are triggered - they don't happen through polling.
Code executes, after code is done, angular triggers a digest.
Example:
element.on('click', function() {
$scope.$apply(function() {
// do some code here, after this, $digest cycle will be triggered
});
});
Angular will also trigger a $digest after the compile/link phase:
Compile > Link > Digest
And as to how many digest cycles are triggered? It depends on how soon the scope variables stabalise. Usually it takes at least 2 cycles to determine that.
Short direct answer to the main question is "NO", angular doesn't automatically trigger digest loop.
TL;DR answer:
Digest loop is designed to run dirty check over POJO models associated with Angular scope instances, so it only needs to run when a model might be changed. In a single page Web application running inside Browser, the following actions/events could lead to a model change
DOM events
XHR responses firing callbacks
Browser's location changes
Timers (setTimout, setInterval) firing the callbacks
Correspondingly, Angular trigger digest loop at, for instance
input directives+ngModel, ngClick, ngMouseOver etc.
$http and $resource
$location
$timeout
Try to answer those bonus questions from my understanding:
ngModel directive often used with angular input directives (text, select etc) together, and the laters will listen on "change" events and call $setViewValue API exposed from ngModelController in order to sync back dom value. During the sync process, ngModelController will make sure to trigger digest loop.
digest loop is different from JS event loop, the later is concept of JS runtime (checkout the great visualised session https://www.youtube.com/watch?v=8aGhZQkoFbQ) which run against event queue and remove consumed event from the queue automatically, but digest loop never remove watch from its watch list unless you explicitly unwatch.
the number of digest loops per second depends on the efficiency of all watch callbacks being executed through the loop . If some bad code took one second to finish, then this digest loop would cost more than 1 sec.
So some key practices for avoid angular performance pitfalls are
Watch callback should be coded as simpler/efficient as possible, for example detach complicated algorithm code to, for example, worker threads
Proactively remove a watch if it is not used anymore
Prefer to call $scope.$digest() instead of $scope.$apply() if applicable, $digest() only run part of scope tree and ensure the models associated under the subtree is reflecting to view. But $apply() will run against entire scope tree, it will iterate through more watches.
I believe this is what happens. AngularJS made a smart assumption that model changes happen only on user interaction. These interactions can happen due to
Mouse activity (move, clicked etc)
Keyboard activity (key up, key down etc)
AngularJS directives for the corresponding events wrap the expression execution in $scope.$apply as shown by #pixelbits in his example. This results in digest cycle.
There are some other events too where AngularJS triggers the digest loop. $timeout service and the $interval service are two such examples. Code wrapped in these service also results in digest loop to run.
There maybe be some other events\services that can cause digest cycles to execute but these are the major ones.
This is the very reason that changes to model outside the Angular context does not update the watches and bindings. So one needs to explicitly call $scope.$apply. We do it all the time when integrating with jQuery plugins.

javascript parallelism

Well, first I want to say I'm a bit new in the world of Internet dev.
Anyway, I'm trying to know if its possible to run two pieces of code in parallel using javascript.
What I really need is to call two methods that are in a remote server. I pass, for both, a callback function that will be executed soon the data I want is ready. As the server running these functions take a time to answer, I'm trying to find a way to call both methods at the same time without need to wait till the first finishes to call the second.
Does methods like setTimeout run concurrently, for example
setTimeout(func1, 0);
setTimeout(func2, 0);
...
function func1()
{
webMethod1(function() {alert("function 1 returned"); } );
}
function func1()
{
webMethod2(function() {alert("function 2 returned"); } );
}
Edited
I've just found this article that may be very cool for the realease of next browsers: Javascript web workers
There is one single thread of execution in Javascript in normal WebBrowsers: your timer handlers will be called serially. Your approach using timers will work in the case you present.
There is a nice piece of documentation on timers by John Resig (author of the very popular jQuery javascript framework - if you are new to Web development, I would suggest you look it up).
Now, if you are referring to HTML5 based browsers, at some point, they should have threading support.
Yes, that's exactly how web requests through AJAX work. No need to setTimeout to 0, you can just call them one by one, and make an AJAX request, and it'll be executed asynchronously, allowing you to pass a callback function to be invoked when the request completes.
The means of creating an AJAX request differs some depending on what browser you're running. If you're going to build something that depends considerably upon AJAX, and you want it to work across multiple browsers, you're best off with a library. Here's how it's done in jQuery, for instance:
$.ajax({ url: '/webrequesturl', success: function(result) {
// this will be called upon a successful request
} });
$.ajax({ url: '/webrequest2url', success: function(result) {
// this will be called upon a successful request
// this may or may not be called before the above one, depending on how long it takes for the requests to finish.
} });
Well, JavaScript is single-threaded, the two timers will run sequentially one after the other, even if you don't notice it.
I would recommend you to give a look to the following article, it really explains how timers and asynchronous events work, it will also help you to understand the single-threaded nature of JavaScript:
How JavaScript Timers Work
And as an alternative you could give a look to WebWorkers, is a way to run scripts in separate background threads, but they are only supported by modern browsers.
What you are looking for is asynchronous client-server communication (keyword: async). Asynchronous functions return straight away, but the provided callback will be executed after the specified condition is satisfied.
So, if the function that sends a request to the server is asynchronous, this would let you send both requests to the server without waiting for one to respond.
Using setTimeout may work, as this will schedule both request-sending functions to be called. However, some browsers only run one thread of Javascript at a time, so the result would be that one of the scheduled functions would run and block (waiting for a reply) and the other scheduled function would wait until the first was done to start running.
It is advisable to use async support from your server communication library. For instance jQuery uses async by default.
It depends on the JavaScript engine.

Categories