Pretty good article, especially in regards to the $digest cycle which can cause some performance migraines. This is something I have seen newbies in Angular encounter pretty quickly, too many watchers and mistakenly triggering tonnes of $digest cycles.
Regarding point 7.1 - sometimes in my experience, an application might have many elements inside of an ng-repeat which cannot be avoided. It is one thing to say keep your lists small, but when you're dealing with a whole bunch of data that is loaded in via an infinite scroll, you can't just stop the content at 50 results.
Honestly, the only solution the team I currently work with have come up with is using React.js (swapping out Angular for React completely would be too expensive). If you take the heavy UI work away from Angular and use React.js, it honestly makes your lives easier. Even just for rendering a whole lot of content inside of an ng-repeat, you will see the performance issues vanish. Using track by in your ng-loops will also save you more migraines.
Don't get me wrong, Angular without-a-doubt has some issues, but there hasn't really been an issue that the team hasn't been able to work around so far. Most issues you encounter in Angular are caused by a limited understanding of the framework and its strengths. The documentation is pretty bad, but you find the more you use Angular, the better you get at it.
Most of the limitations you encounter in Angular, they are in every single other front-end framework as well. This is because browsers currently do not support some of the niceties in ES6 and many of us have to support older browsers like IE9, etc. But even so, ES6 won't fix everything, but it will make things a lot less painful. Things are getting better, but front-end frameworks are just a little too ahead of their time at the moment.
While Angular has issues now with performance (mainly two way binding and watching) when ES6 is supported in Angular 2.0 and object.observe() is used, we are going to see a dramatically more powerful framework without all of the dirty checking Angular currently has to make binding and watching work. This will be the case for other frameworks like Knockout and Ember as well.
Yeah, I had the same experience (except using Mithril instead of React).
Am I the only one not holding my breath for Object.observe? I see people mention it so casually, but the fact is it's still a long way from being anywhere close to production ready[1]
We'ere using KnockoutJS, not Angular in our app, and it suffers from the same issues. On certain components, especially when applying complex rendering logic, that need to be optimized, like our DataGrids, we've been moving towards a React-based rending flow (might eventually do it serverside). The React version is about 8x faster than our Knockout implementation.
The angular core team has mentioned that they may have overstated the performance gains of Object.observe(). I don't think there'll be as large performance gains as people think.
Even once it's widely available, it'll probably take a long time to optimize.
On a personal note, after using angular for around two years... You can write good angular code, but in my experience, a lot of people (especially libraries) are writing really bad stuff. I've been playing around with this whole Flux+React ecosystem and I'm starting to think the patterns it presents are more sensible.
On top of that, this whole magic of two-way data-binding is certainly cool and fun, but I noticed that for most cases I know exactly when a value is changing, so notifying of a change manually is not a big deal (even if it's more work, it usually brings good performance). I'd be really happy if angular had a built-in way of doing something like event-based bindings. Right now we have regular bindings, and one-time bindings. I want a way to bind to a property where it'll wait for the property to stabilize and then remove the watcher unless I manually notify it that it changed.
So here's a trivial example: let's say I have a Stack Overflow styled app. When I open a post, it'll load the title, question, and answers. Now I could use one-time bindings so the title and question aren't being watched anymore. But I know that after loading this page I could receive a websocket notification that the title has been updated. I'm going to have to keep a watcher on the title even though it can only change in that one condition! It'd be great to just have something built in to handle this. (And I can think of many ways to solve this, such as making my own directive in which I add a listener for an event and then update it manually; but it'd be great if we had a built-in solution.)
That is actually a great idea. Things are a little busy in the lead up to the holidays, but perhaps in the New Year, we could write up a little article detailing the quirks we encountered and how we fixed them. We have learned a lot of the last year or so.
If you could show some example of views before and after optimization, that would be great! I haven't yet had to sit down and optimize beyond basic best practices so it would be valuable to see some actual coded, real world example.
Not everyone has the luxury of throwing away one years worth of work and choosing something else.
Once you understand how Angular works internally and not just in a tutorial sense, it can be very powerful. Most of Angular's quirks can be worked around using common sense, and as in some cases, using the right tools, understanding Angular isn't a silver bullet goes a long way too. The issues my team encountered won't be encountered by everyone, we were dealing with potentially thousands of items being in the page caused by infinite scrolling.
I get that some people think Angular is difficult, learning the basics is easy, learning how to use Angular the right way is the hard part. I think a lot of the issues people encounter using Angular are due to their own limited understanding of the framework. The documentation leaves a lot to be desired, but there are a lot of good blog posts out there like this which tell you upfront the issues and best practices.
The beautiful thing about Angular is that it is not difficult to get it to shows its warts, so you can work around or fix them. Combined with Batarang, you can get a pretty insightful look into your application and where you can improve it as well. A lot of the issues we've encountered were due to the fact someone on the team did something wrong expecting it to work, not Angular itself. Like a good woodworker, you've got to go with the grain, not against it.
I think if our application were being built from the ground up today, the team unanimously agrees that we would use something like React + Flux. But in our case, the business wouldn't be too happy with us throwing away old unit tested and battle-tested code in favour of something new and lean for the sake of a few milliseconds of browser performance.
I agree 100% with this. Every framework has its pain points - that is why it limits you as a framework, as opposed to homerolling your own solution.
The gain you get is better support for foundational tasks and the ability to focus on the high level business requirements faster. There are very smart people working on the teams of all of these frameworks, available for free with an MIT license and supported for free - take advantage of it, but don't expect any of them to be perfect and expect to still be putting in hard work in understanding the internals of all of them.
>There are very smart people working on the teams of all of these frameworks
Dunno, most of the JS frameworks have crappy design and mediocre implementations. Angular is not some shining beacon, it's convoluted beyond belief.
Being a framework, and being from a large company doesn't mean much, nor it guarantees that the result reflects having been worked by "smart people".
The SUN people were smart too, and yet we got the J2EE shit-fest of early to mid 00's. IBM also shipped it's share of crappy frameworks, Microsoft too.
>and expect to still be putting in hard work in understanding the internals of all of them.
Also expect those framework based skills to be obsolete 4-5 years down the line, when some other shiny framework comes along.
Also expect to have to rewrite tons of your work, when the developers with ADD lose interest and go to rewrite the framework, like with Angular 2 (and I've relived these all over again with (Java) Struts 2, (Python) Zope 3, and tons of other stuff besides).
Nit-pick, but I don't agree with his definitions of complexity.
>>> The second reason software is slow is known as space complexity. This is a measure of how much 'space' or memory a computer needs to run your solution. The more memory required, the slower the solution.
Requiring more memory doesn't make an application slower by default. It simply means it requires more memory to run as expected.
Similarly, when he's talking about run time complexity, he calls it "a measure of how many comparisons a program needs to make to achieve a result". It can be any action, not necessarily a comparison. Be it number of times cycle needs to be executed, number of expensive calls to external service, etc. Number of comparisons to make is commonly used in introductory courses when studying sorting algorithm but doesn't make much sense without a context.
> 6.2 Watching Functions
>
> Another common problem is the utilization of functions
> in watchers or bindings. Never bind anything (ng-show,
> ng-repeat, etc.) directly to a function. Never watch
> a function result directly. This function will run on
> every digest cycle, possibly slowing your application
> to a crawl.
A while ago this sort of advice was true (when you'd see people inlining stuff in `eval` to avoid this), but nowadays function execution itself is very fast.
You don't want to watch a slow function, but just watching a function will not "[slow] your application to a crawl."
Interesting post, but I'd take this author's advice with some salt.
I disagree strongly as well. For example if you have something like ng-show="is_admin()" and $scope.is_admin = function() { return $scope.logged_in && $scope.role='admin' } or some other complexity reducing function, I don't understand how that would be significantly less performant than copying and pasting the return statement everywhere in your html.
Requiring more memory doesn't make an application slower by default.
On a cacheless system, that would be true. On a typical system with multilevel caches (and an application that does not fit completely in the smallest one), the number of cache misses will tend to go up as the memory usage increases, and that will affect performance. At the extreme end, when it's too big to fit completely in physical memory and the swapfile starts being used, the speed difference becomes night and day.
Can somebody explain me what "large Applications" are?
I read it many times, but when i compare it to the "little" Tools i write for my company, it seems that this "large" Applications are really really tiny.
So what did you understand under large Applications?
Large should be at least 80k lines of code, preferably more than 100k.
Another metric I go by is there is no one on team have overlapping knowledge of the code base. In other words, no other teammate is suited to review your code because they have no context of it.
By the second metric, Google is not a "large" software system because all code must be reviewed by someone with context on it. (Single-developer projects and codebases don't get anywhere at Google...I tried, multiple times.)
I think a decent definition is "software where no one person has the whole system in her head", i.e. where reading existing code takes as least as much time as writing code, and any change may have unforeseen ramifications on the system as a whole.
> Single-developer projects and codebases don't get anywhere at Google.
As a Googler working on a single-developer project / codebase, I'm sorry to hear that you didn't get traction. Very grateful for my mostly autonomous role :) There are tradeoffs of course, but to me the reduced overhead of no meetings and greater creative control are worth the trouble of an increased workload and providing support.
Good luck. I really loved the experience as well, it's just that the project got canceled when my management sponsor moved to a different department and his replacement was like "No more one-off projects." The funny thing is - I'd been warned by some very senior and very accomplished (outside of Google) people that independent projects basically never launch, but since my manager was very supportive, I'd discounted the warnings.
> services and object references to propagate object changes between scopes.
There are so many ways to wire things up in Angular -- references, watches, broadcast/emit -- that I'd love to see some examples from the author, who seems to really know what he's doing.
For example, say I have a `userService` that maintains a small `user` model with username, name and roles properties. What's the most performant and maintainable way of "binding" to changes to this object so when I call, e.g.: `userService.logout()` somewhere, some component I write can pickup the change and adjust it's state / UI accordingly?
I think the only way to do what you want with your service is by injecting rootScope and using $emit, since we're trying to capture a "logout" event and tell our other controllers about it.
Reacting to the specific event would likely waste less resources than pulling out the $watch or $watchCollection sledgehammer.
What works well for me is having the service return an object (named `data`) alongside the functions like `logout`, which contains another object with the actual model (`user`). Then, in controllers, I assign it to $scope.data and $watch('data.user') which reflects the changes in the controller since it this object is being shared by reference among all controllers and directive that inject the service.
Create an angular factory with a standard observer pattern. So you register the callback with something like MyFactory.register(updateUI()); and call MyFactory.notify(); after userService.logout(); which calls all the registered functions.
If you aren't going to use $scope.$watch, you might want to ask why you're using Angular in the first place. From my own experience building a large-ish hybrid mobile app in Angular, I haven't run into any performance issues. While you should of course understand the implications of how you structure your application, I would also warn against premature optimization. I advise building first for simplicity and ease of maintenance and then tweak for performance if needed.
$scope.$watch()ing large arrays unnecessarily will be the death of you
Using ng-show where there is complex render logic in that element, consider ng-ifs to remove from DOM completely
Using track by in ng-repeats can help
We had simple screens which would hide any symptoms of bad practices but once you have an 'overview' screen with 10 different lists, many different show/hide forms etc the page can get slow very quickly.
I've been using this SO answer[0] as a list of good resources. The egghead.io videos have been most helpful to me, I really enjoy the "bite-sized" videos style and the way the speaker explains how attributes/vars relate to others in the application. Here's their specific page for beginning AngularJS.[1]
Anywhere and everywhere. The Angular docs are notoriously shit sometimes, so finding as many examples as you can from other sources, then deducing the "right way" from those has been the best help for me.
The best resource I found was using ng boilerplate.
If anyone, like me, didn't know about 'track by' as a way to optimize ng-repeats, here's a couple of articles that explain what it is and why it's beneficial:
9.4 seems to be an outdated issue. Since version 1.2.16, $rootScope.$broadcast does not go to all child scopes, only those with a watcher. It's very fast, and perfectly fine to use if you need it.
My impression is that Angular-specific issues are way more important than pure Javascript speed concerns in terms of relative impact on an application. Number of watchers and ng-repeat declarations are the most important aspect of an AngularJS application.
I disagree with 9.4 as I think you should try to use event emitting more. This is the best way to modularize your application and really separate your concerns.
For example, given a service in charge of dealing with the a part of user's data. All mutations should be done through the helpers given by the service and then it should emit an event telling everyone subscribed to deal with it and update themselves.
Doing it this way, you maintain much more sanity when you have to create a really reactive application.
>Similarly, Angular provides the ability to watch entire objects by passing a third, optional true parameter to scope.$watch. This is, for lack of better words, a terrible idea. A much better solution is to rely on services and object references to propagate object changes between scopes.
How am I supposed to then propagate these changes into the DOM if I'm not using $watch?
Large and small are fuzzy metrics. A subset of non binary thinking (see Kosko, the model of probability and the hypercube, etc). "Jeremy is short," well - to a 5'0 person, I'm freaking tall.
To Yao Ming, I'm short. Trouble is, we each of us technologists tend towards introversion (been struggling since...well, take my age and then subtract however many years my communication skills were less than stellar).
Ever try to raise money for an idea based on numbers? It's like trying to land a date based on the size of your...real estate. Sooner or later, another yutz shows up, plops his drink on the counter and announces that the yacht he just stepped off was twice the size of yours.
Ya, it's never a fun game because it necessitates that binary outcome. Suffice to say good tools in the wrong hands can still be used to chop off your own feet if you don't pay attention.
Regarding point 7.1 - sometimes in my experience, an application might have many elements inside of an ng-repeat which cannot be avoided. It is one thing to say keep your lists small, but when you're dealing with a whole bunch of data that is loaded in via an infinite scroll, you can't just stop the content at 50 results.
Honestly, the only solution the team I currently work with have come up with is using React.js (swapping out Angular for React completely would be too expensive). If you take the heavy UI work away from Angular and use React.js, it honestly makes your lives easier. Even just for rendering a whole lot of content inside of an ng-repeat, you will see the performance issues vanish. Using track by in your ng-loops will also save you more migraines.
Don't get me wrong, Angular without-a-doubt has some issues, but there hasn't really been an issue that the team hasn't been able to work around so far. Most issues you encounter in Angular are caused by a limited understanding of the framework and its strengths. The documentation is pretty bad, but you find the more you use Angular, the better you get at it.
Most of the limitations you encounter in Angular, they are in every single other front-end framework as well. This is because browsers currently do not support some of the niceties in ES6 and many of us have to support older browsers like IE9, etc. But even so, ES6 won't fix everything, but it will make things a lot less painful. Things are getting better, but front-end frameworks are just a little too ahead of their time at the moment.
While Angular has issues now with performance (mainly two way binding and watching) when ES6 is supported in Angular 2.0 and object.observe() is used, we are going to see a dramatically more powerful framework without all of the dirty checking Angular currently has to make binding and watching work. This will be the case for other frameworks like Knockout and Ember as well.