Coalesce / defer multiple child scope $apply calls - javascript

In my app I am receiving data via an HTTP channel that's handled in a custom way. I'm building some [data] objects from the pipe, wrap them in a scope.$new(true) and when I receive an update call childScope.$apply() to set the new properties.
This works fine for light loads, all the watchers get notified and has really been running without any issues or missed updates.
Now I'm trying to push a lot more updates and don't know if the pattern used above is the way to go. I think (though have not checked) that each call to $apply calls the digest on the root scope and I want to coalesce these on browser cycles or ~50ms intervals. Currently, whenever I receive ~100 updates on 5000 objects/scopes it kills the browser.
I saw that angular docs say each scope has an $applyAsync method but I cannot find it anywhere, this would be essentially what I am after.
Is this a bad idea and the performance is already good enough? Should I implement my own applyAsync method by using $browser.defer() or some other method?
Edit: just tested the code and indeed the $rootScope.$digest is called for each child scope $apply(). Perhaps moving this part away from Angular JS and using a listener-based approach is better, so this is also a valid answer.

In the end I used evalAsync and this seems to work as intended.
I probably need to call $digest (or $apply) every so often to make sure there are no pending scope changes but I have not seen the need to do this yet.
So my idea would be to:
call evalAsync for all the scope changes that need to happen very fast
increment a counter before the evalAsync call
set a variable with the current time inside the evalAsync function parameter and decrement the counter
on a timer (50-100ms) see if the counter is >0 and if the last evaluation time was some time ago (>50-100ms) and if yes force a digest loop.
I will not mark this as a correct answer since it does not seem like the best idea but it was the best I could come up with and it does the job as intended.

Related

Most efficient way to detect a variable changed and execute a callback

I would like to detect when a variable's value is updated on a server and run a callback that sends the new value to a client in order to provide a real-time feed. For example, say a server generates a random number at random intervals, but it's important for the client to be aware of a new number being generated as soon as it happens.
One simple solution is to define a custom function that you use to set your value instead of directly setting it with '=':
function setValue(val) { a = val; websocket.send(a) }
But that requires going back through already written code and replacing all instances of that variable being updated to instead use the custom function. A tedious and error-prone process but only has to be done once.
Another solution that I read about is to define a setter on the variable when initializing it, as described here.
This would only require replacing all the places where the variable is being initialized for the first time, but since it's listening hidden in the background I worry it reduces readability and someone may forget there's a callback firing each time the variable is being updated.
I don't code professionally so I'm not sure which of these solutions would be more widely accepted, or if there's a better way I should be approaching it. Also not sure of the performance hit this would cause but I'm guessing pretty negligible.
Define the custom function. Take the refactor pain now and keep it readable. Coding is 20% creation and 80% maintenance, but it always feels like 99% creation and 1% maintenance when you’re in the thick of making something new.

Is data binding in AngularJS based on time period?

I've already read the answer:
https://stackoverflow.com/a/9693933/4382683
What I want to know is when does angular decide that it's suitable to run the $digest cycle? Because when I change a property of an object in $scope, it's instantly propagated throughout. Does this dirty-checking happen every 50ms or so?
Also, is Object.watch() used anywhere in Angular?
It's not based on time period. Angular actually $digests only if one of the following happens:
Any interaction with ng tags and attribute or custom directives
When a promise is resolved. Like for $http or $timeout
These and probably a few other cases I don't know about. I understand now that it's not possible that the values in $scope change without any of these things happening. Hence, whenever these happen, angular does a $digest. Also, no time periods.
And, no Object.watch() anywhere since it's only for debugging purposes.

Updating a resource in Vue JS reactively?

I want to save form data to my backend every time the user changes the data on the page, (ideally with a timeout to wait until the user has stopped making changes and then save it).
I've thought of a few ways of handling this. I could put change events on all the fields to do so. I could also try and use a computed variable and add a call to the set method every time the object is set but this seems overly complex.
I haven't seen many examples of people doing this with Vue JS. Is there a clear way to do it? Is there a way to simply say upon the data object changing, run the following function?
Use the debounce attribute on your inputs:
https://vuejs.org/guide/forms.html#debounce
Then you can use the watch() method as Yerko suggested. The attributes in Vue won't be updated until after the timeout, at which point your watch function will fire.
If that isn't strong enough, you'd have to save the setTimeout variable and then cancel it and restart it in your watch function using clearTimeout. This isn't as complex as it sounds, just an extra variable and a deep watch function.

AngularJS and location.path()

I'm working on a webpage based on AngularJS and sometimes I need to change the path (the shebang if you prefer it). The thing is that sometimes $location.path("/my_path_here") works, but sometimes I need to call $scope.$apply() after calling $location.path to make the webbrowser switch to the new path.
Why is this happening?
EDIT:
Example http://pastebin.com/d1SjfCHd
Take a look at this question and Misko's answer: How does data binding work in AngularJS?
That answer explains the process in technical great detail. So, I'm gonna tell it in layman's terms.
AngularJS makes itself work by using dirty checking, there are sets of values that angular is watching. Everytime something big happens, (a click, a function call in controller), angular runs a digest cycle, comparing the watched values to see if there is any change. If there is a change, depending on the watcher, angular will take necessary action (update view, fire a callback, update route).
When you use default directives and no jquery event handling in controller, you will be fine.
However, if you do, you need to know that you are NOT in the angular's digest cycle. Which means the watchers will not fire until a digest occurs.
You need to know when are you updating a variable that is being watched. Mostly it is custom DOM event (or jquery events). When it is the case, you need to let angular know that something is changed and it needs to re-check what happened (ie. update watchers).
This is by doing scope.$apply() after you have changed something.
Bear in mind that you cannot run an $apply() if you are already in the angular's digest cycle. It will throw an error like $digest already in progress.

AngularJS: How do you create app wide variable that updates itself

I am working on a small game/simulation app and I need an app-wide time variable that updates itself.
It could just as well be a number variable that keeps increasing for all I care, e.g. 0..1..2..3..4 every second to represent time progress within this simulation app. (this doesn't have to be JavaScript Date Object per se)
I want it to be accessable in every controller/service I inject it into and to automatically update in the whole app. Other scope variables that depend on this time variable would then automatically update with all the two-way-binding goodies that AngularJS presents.
Is this even a 'legal' line of though within AngularJS app dev philosophy? If it is, what would be the best way how to construct it?
You could either build a special services for that which can be injected everywhere where it is needed or you just put it on $rootScope. Normally you should avoid putting things on the $rootScope as global state is often a code smell. However, if that's really what you need in your case, then this might suite your needs.
You can simply put that code on a run block so it get's executed as soon as the module is loaded.
myModule.run(['$rootScope', function($rootScope){
setInterval(function(){
$rootScope.now = new Date().getTime();
}, 1000)
}]}
Be aware that in the fashion I wrote the code above, Angular won't be notified about that change. However, notifying Angular every second about that change might not be what your want as it could lead to serious performance problems. Just to clarify things: If you do want Angular to be notified about the change and update all bindings, then the variable update would need to be wrapped in a $apply call.
myModule.run(['$rootScope', function($rootScope){
setInterval(function(){
$rootScope.$apply(function(){
$rootScope.now = new Date().getTime();
});
}, 1000)
}]}
Also be aware that most of the time you want to avoid setInterval as the time between the processing can be non deterministic. Most of the time setTimeout is better suited. It's out of the scope of this answer to dive into that but if you want to read up on that topic I recommend reading John Resig's post on the topic: http://ejohn.org/blog/how-javascript-timers-work/
When i understand your question right you search for $rootScope.
In each Controller/Directive/Service where you include the rootScope you can globally throw stuff.
Click here for the awesome AngularJS-Docu ;)
Use of $rootScope for global variables is a code smell that should be avoided, if possible.
A better solution is using a factory/service, which you can inject into whatever components that need it.
See https://stackoverflow.com/a/18882147/2596608 for more details

Categories

Resources