<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title></title>
    <description>Elad Shahar&#39;s personal blog and website</description>
    <link>https://blog.eladshahar.com/</link>
    <atom:link href="https://blog.eladshahar.com/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Mon, 10 Aug 2015 20:24:44 +0000</pubDate>
    <lastBuildDate>Mon, 10 Aug 2015 20:24:44 +0000</lastBuildDate>
    <generator>Jekyll v2.4.0</generator>
    
      <item>
        <title>Performant Array Computed Properties</title>
        <description>&lt;p&gt;Take a look at the following examples of a &lt;code&gt;remainingTodos&lt;/code&gt; array computed property:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Without prototype extensions -- will be using this style for the rest of the post&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;remainingTodos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Ember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;todos.@each.isDone&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;todos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;todos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;todos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filterBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;isDone&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// With prototype extensions&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;remainingTodos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;todos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;todos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;todos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filterBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;isDone&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;todos.@each.isDone&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// With a computed macro&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;remainingTodos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Ember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filterBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;todos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;isDone&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example we are filtering an array of &lt;code&gt;Todo&lt;/code&gt;s to only those with an &lt;code&gt;isDone&lt;/code&gt; of &lt;code&gt;false&lt;/code&gt;. While this seems simple and correct (and indeed is), it has the potential to become a performance bottleneck!&lt;/p&gt;

&lt;p&gt;In fact, this potential exists for almost every computed property that has an array as a dependency with &lt;code&gt;@each&lt;/code&gt;. This is exacerbated in situations where the array is subjected to multiple stages of computed properties (sorting, filtering, one or more mappings).&lt;/p&gt;

&lt;p&gt;One of my applications has to load thousands of rows of data and do some simple transformations, which should be no problem for a modern browser. Despite this we started receiving bug reports that our UI load times &lt;a href=&quot;assets/images/performant-array-cps/slow.png&quot;&gt;exceeded 3 seconds in some cases&lt;/a&gt;, sometimes even (for some more extreme cases) taking several minutes or more! We tried reducing the number of items that were rendered at once (with paging or &lt;a href=&quot;https://github.com/emberjs/list-view&quot;&gt;&lt;code&gt;ember-list-view&lt;/code&gt;&lt;/a&gt;) but that seemed to have no effect.&lt;/p&gt;

&lt;p&gt;I asked about this on the &lt;a href=&quot;https://ember-community-slackin.herokuapp.com/&quot;&gt;Ember Community Slack&lt;/a&gt;, and I was lucky to have &lt;a href=&quot;https://github.com/mmun&quot;&gt;Martin Muñoz (mmun)&lt;/a&gt; take the time to explain how array computed properties work and how to use them efficiently. After applying what we learned, we were able to reduce our UI load time to &lt;a href=&quot;assets/images/performant-array-cps/fast.png&quot;&gt;under half a second in all but the most extreme cases&lt;/a&gt; (of course, there’s always more work to be done!). Much of this post is summarizing our discussion.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;observers&quot;&gt;Observers&lt;/h2&gt;

&lt;p&gt;Though this post is primarily focused on computed properties, &lt;code&gt;Ember.Observer&lt;/code&gt;s use a similar dependency declaration and much of this applies to them, too. If you’re using observers, though, you may want to consider &lt;a href=&quot;https://www.youtube.com/watch?v=vvZEddrClAQ&quot;&gt;whether an observer is the right answer to your problem&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;quick-review&quot;&gt;Quick Review&lt;/h2&gt;

&lt;p&gt;Before we can discuss how to get the most out of your array computed properties, let’s review the array dependency options we have available to us today and when they’d trigger recomputation.&lt;/p&gt;

&lt;p&gt;If you’re already familiar with array computed properties and the difference between &lt;code&gt;array.[]&lt;/code&gt; and &lt;code&gt;array.length&lt;/code&gt;, feel free to &lt;a href=&quot;#achieving-performance&quot;&gt;skip ahead&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;array&quot;&gt;&lt;code&gt;array&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;The most basic is treating an array like any other property. This approach works great for most cases, but will only trigger a recomputation when the array object is changed. That is, the whole array has to be swapped out for another array.&lt;/p&gt;

&lt;h3 id=&quot;arraylength&quot;&gt;&lt;code&gt;array.length&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Sometimes knowing when an array is swapped out isn’t enough; you also want to recompute when the length of the array changes. This is where &lt;code&gt;array.length&lt;/code&gt; is helpful. This is useful if your property should update when the length of the array changes but the content of the array doesn’t matter. One example of this is showing a total count (“You have 3 Todo items left”).&lt;/p&gt;

&lt;p&gt;The important thing to note is that this &lt;a href=&quot;https://github.com/emberjs/ember.js/blob/v1.13.5/packages/ember-runtime/lib/mixins/enumerable.js#L1123-L1127&quot;&gt;won’t trigger if the array length doesn’t change&lt;/a&gt;, even if items are added or removed from the array.&lt;/p&gt;

&lt;h3 id=&quot;array-1&quot;&gt;&lt;code&gt;array.[]&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;And that’s where &lt;code&gt;.[]&lt;/code&gt; comes in. It does all of the above, but also guarantees it will trigger whenever items are added or removed from the array (even if the length doesn’t end up changing).&lt;/p&gt;

&lt;h3 id=&quot;arrayeachsomeprop&quot;&gt;&lt;code&gt;array.@each.someProp&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;For many properties, knowing when items are added/removed from the array doesn’t cut it; you also need to know when properties on items in the array change. For example, you might be summing up a bunch of values, or providing a filtered version of the array (as we do in &lt;code&gt;remainingTodos&lt;/code&gt; above).&lt;/p&gt;

&lt;p&gt;In these cases we care about the array being swapped out, items being added to or removed from the array, and if a relevant property changes on any item in the array. We can use &lt;code&gt;.@each.someProp&lt;/code&gt; to accomplish this.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;@each&lt;/code&gt; is only supported once per dependency string, so you can’t do something like &lt;code&gt;students.@each.assignments.@each.grade&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;arrayeachsomepropanotherprop&quot;&gt;&lt;code&gt;array.@each.{someProp,anotherProp}&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Property brace expansion was &lt;a href=&quot;http://emberjs.com/blog/2014/02/12/ember-1-4-0-and-ember-1-5-0-beta-released.html#toc_property-brace-expansion&quot;&gt;added in Ember 1.4&lt;/a&gt; and provides a shorthand for observing multiple properties on the same object.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// With property brace expansion&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;Ember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;array.@each.{foo,bar,baz}&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Without property brace expansion&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;Ember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;array.@each.foo&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;array.@each.bar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;array.@each.baz&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can even do &lt;code&gt;{some,many}.really.{crazy,interesting}.things&lt;/code&gt;, though you probably shouldn’t.&lt;/p&gt;

&lt;h3 id=&quot;arrayeach&quot;&gt;&lt;code&gt;array.@each&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;You may occasionally see &lt;code&gt;@each&lt;/code&gt; at the end of a dependency as a leaf property. This works and is equivalent in behavior to &lt;code&gt;array.[]&lt;/code&gt;, but has much worse performance and should be avoided.&lt;/p&gt;

&lt;h3 id=&quot;matrix&quot;&gt;Matrix&lt;/h3&gt;

&lt;p&gt;Let’s take a look at some operations you might perform with arrays and which dependency patterns they’d trigger:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Operation&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;code&gt;array&lt;/code&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;code&gt;array.length&lt;/code&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;code&gt;array.[]&lt;/code&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;code&gt;array.@each.foo&lt;/code&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;this.set(&#39;array&#39;, newArray)&lt;/code&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;array.pushObject(newObject)&lt;/code&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✘&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;array.removeObject(newObject)&lt;/code&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✘&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;array.setObjects(newArray)&lt;/code&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✘&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;array.replace(0, 1, [ newObject ])&lt;/code&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✘&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✘&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;array.set(&#39;firstObject.foo&#39;, 3)&lt;/code&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✘&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✘&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✘&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✔&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;achieving-performance&quot;&gt;Achieving Performance&lt;/h2&gt;

&lt;p&gt;You don’t have to concern yourself when depending on &lt;code&gt;array&lt;/code&gt;, &lt;code&gt;array.length&lt;/code&gt;, or &lt;code&gt;array.[]&lt;/code&gt;. But if you’re using &lt;code&gt;array.@each.someProp&lt;/code&gt;, you may need to take one of the following steps to avoid a performance overhead if you expect a need to deal with very large arrays.&lt;/p&gt;

&lt;p&gt;The key to performance when dealing with arrays is using the correct array dependencies for your application. Lots of work has been done to help reduce rendering work (&lt;em&gt;e.g.&lt;/em&gt;  &lt;a href=&quot;https://github.com/emberjs/list-view&quot;&gt;&lt;code&gt;ember-list-view&lt;/code&gt;&lt;/a&gt;), but sometimes the most intensive work is data processing. As it turns out, even when the operations you are conducting on the array are extremely efficient and fast, the overhead from attaching observers to each object in an array adds up and can quickly become nontrivial.&lt;/p&gt;

&lt;p&gt;We can address this in one of two ways. Sometimes we can get away with a simpler dependency; other times we have to use the little-known &lt;code&gt;notifyPropertyChange&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;simplifying-dependencies&quot;&gt;Simplifying Dependencies&lt;/h3&gt;

&lt;p&gt;My initial mental model of computed property dependencies was that a computed property should depend on whatever values it uses in its computation. We saw this above with &lt;code&gt;remainingTodos&lt;/code&gt; depending on the &lt;code&gt;isDone&lt;/code&gt; property of every &lt;code&gt;Todo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This fit my understanding of a property, such as &lt;code&gt;isDone&lt;/code&gt;, being changed in any way (a push from the back-end, someone opening Ember Inspector and tinkering with data, etc.) causing the UI to update and reflect the new state appropriately.&lt;/p&gt;

&lt;p&gt;Unfortunately, while using &lt;code&gt;@each&lt;/code&gt; to depend on properties that are part of the calculation seems “correct,” this approach turns out to be naïve in some cases. This is especially true if your array goes through several transformation steps which use &lt;code&gt;@each&lt;/code&gt;. In our application we had a &lt;code&gt;filter&lt;/code&gt; step, a &lt;code&gt;sort&lt;/code&gt; step, another &lt;code&gt;filter&lt;/code&gt; step, and then several &lt;code&gt;map&lt;/code&gt;s. The overhead from observing every item in every array at every step slowed our app to a crawl.&lt;/p&gt;

&lt;p&gt;Consider how values in your application change under typical usage. How can the objects in the array change? How can their properties? Some properties might be user configurable, but you might find that some computed properties depend on data that does not change (during normal usage).&lt;/p&gt;

&lt;p&gt;Our application, being a graph-based dashboard, was mostly visual representations of time series data. Only minimal aspects of our model were expected to change, and data was updated by a new data array replacing the old array. In this case, despite &lt;code&gt;array.@each.someProp&lt;/code&gt; as a dependency feeling “correct,” it was unnecessary as &lt;code&gt;array&lt;/code&gt; worked just as well.&lt;/p&gt;

&lt;p&gt;In our case, we were able to simplify our dependencies to avoid using &lt;code&gt;@each&lt;/code&gt; for properties that never change independently of the object/array, and our app became fast again. The question to ask yourself is whether or not these properties will change under normal application usage.&lt;/p&gt;

&lt;p&gt;Sometimes, however, normal application usage includes changing properties on objects in an array. For example, we’d expect &lt;code&gt;isDone&lt;/code&gt; to change on &lt;code&gt;Todo&lt;/code&gt; objects in the &lt;code&gt;todos&lt;/code&gt; array. In this case, changing &lt;code&gt;isDone&lt;/code&gt; should cause &lt;code&gt;remainingTodos&lt;/code&gt; to recompute, and simplifying the dependency to just observe &lt;code&gt;todos&lt;/code&gt; wouldn’t work. In these circumstances, we can use &lt;code&gt;notifyPropertyChange&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;using-notifypropertychange&quot;&gt;Using &lt;code&gt;notifyPropertyChange&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Every class that implements &lt;code&gt;Ember.Observable&lt;/code&gt; (including &lt;code&gt;Ember.Object&lt;/code&gt;, &lt;code&gt;Ember.Array&lt;/code&gt;, &lt;code&gt;Ember.Component&lt;/code&gt;, and most Ember classes you’re likely to be working with) comes with a very useful &lt;code&gt;notifyPropertyChange&lt;/code&gt; method. This method allows you to, as the name suggests, trigger a change notification even if an object or property hasn’t changed! In fact, &lt;a href=&quot;https://github.com/emberjs/ember.js/blob/v1.13.5/packages/ember-runtime/lib/mixins/enumerable.js#L1123-L1127&quot;&gt;this is how &lt;code&gt;.[]&lt;/code&gt; and &lt;code&gt;.length&lt;/code&gt; work within Ember&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We can use this to avoid having to observe all the objects in an array for property changes and get away with depending only on &lt;code&gt;array&lt;/code&gt; and get the performance benefits of not having to set up all those observers.&lt;/p&gt;

&lt;p&gt;We can do this by, anywhere in our app where we change a property that should trigger recomputation, calling &lt;code&gt;this.notifyPropertyChange(&#39;array&#39;)&lt;/code&gt;. For example, here’s how we might implement some actions that handle &lt;code&gt;Todo&lt;/code&gt; toggling:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;completeAll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;todos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;isDone&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;notifyPropertyChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;todos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;toggleTodo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;todo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;isDone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;todo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;isDone&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;isDone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;notifyPropertyChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;todos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can then rewrite our &lt;code&gt;remainingTodos&lt;/code&gt; to avoid using &lt;code&gt;@each&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;remainingTodos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Ember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;todos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;todos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;todos&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;todos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filterBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;isDone&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While this approach can improve your performance, remember that it comes with some future refactoring risk:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You must be careful to always call &lt;code&gt;notifyPropertyChange&lt;/code&gt; in each action you add or computed properties won’t update.&lt;/li&gt;
  &lt;li&gt;It may cause computed properties which only care about the array being swapped out to needlessly recompute.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;If you’re building a small- to medium-sized Ember.js application, you likely don’t have to worry about array observer overhead and should choose dependencies based on what values a computed property uses in its calculation.&lt;/p&gt;

&lt;p&gt;If your app deals with very large arrays, you may have to employ one of the more advanced techniques: simplifying dependencies to avoid &lt;code&gt;@each&lt;/code&gt; or using &lt;code&gt;notifyPropertyChange&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The difference between these two mental models is the change of perspective from asking “what is needed for the computation of this property?” to “what can a user change that would result in this property needing to be recomputed?”&lt;/p&gt;
</description>
        <pubDate>Mon, 10 Aug 2015 00:00:00 +0000</pubDate>
        <link>https://blog.eladshahar.com/performant-array-computed-properties.html</link>
        <guid isPermaLink="true">https://blog.eladshahar.com/performant-array-computed-properties.html</guid>
        
        <category>ember</category>
        
        
        <category>ember</category>
        
      </item>
    
  </channel>
</rss>
