Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Bringing Immutability to Angular

Bringing Immutability to Angular

Some ideas from the functional paradigm, such as pure functions and immutable data, constantly find broader applications in building user interface.

One of the biggest showdowns in an AngularJS application is caused by the evaluation of hundreds of expressions inside the $digest loop. Using immutable collections can help us reduce the running time of some of them. The unidirectional data flow in Angular 2 allows us to take advantage of the “pure components” for further optimizations in our applications.

In this talk I’m going to make an introduction to how we can take advantage of immutable data structures in our Angular 1 and 2 applications.

Minko Gechev

May 07, 2015
Tweet

More Decks by Minko Gechev

Other Decks in Programming

Transcript

  1. Minko Gechev github.com/mgechev twitter.com/mgechev { "job": "Freelancer", "hobbies": [ "open

    source", "blogging", "teaching", "sports" ], "communityEvents": [ "SofiaJS", "BeerJS Sofia" ] }
  2. $watch // ... // Most common watches are on primitives,

    in which case we can short // circuit it with === operator, only when === fails do we use .equals if (watch) { if ((value = watch.get(current)) !== (last = watch.last) && !(watch.eq ? equals(value, last) : (typeof value === 'number' && typeof last === 'number' && isNaN(value) && isNaN(last)))) { // ...
  3. // ... // Most common watches are on primitives, in

    which case we can short // circuit it with === operator, only when === fails do we use .equals if (watch) { if ((value = watch.get(current)) !== (last = watch.last) && !(watch.eq ? equals(value, last) : (typeof value === 'number' && typeof last === 'number' && isNaN(value) && isNaN(last)))) { // ... $watch could be slow…
  4. // ... // Most common watches are on primitives, in

    which case we can short // circuit it with === operator, only when === fails do we use .equals if (watch) { if ((value = watch.get(current)) !== (last = watch.last) && !(watch.eq ? equals(value, last) : (typeof value === 'number' && typeof last === 'number' && isNaN(value) && isNaN(last)))) { // ... $watch or much faster…
  5. $watchCollection // ... for (var i = 0; i <

    newLength; i++) { oldItem = oldValue[i]; newItem = newValue[i]; bothNaN = (oldItem !== oldItem) && (newItem !== newItem); if (!bothNaN && (oldItem !== newItem)) { changeDetected++; oldValue[i] = newItem; } } // ...
  6. $watchCollection // ... for (var i = 0; i <

    newLength; i++) { oldItem = oldValue[i]; newItem = newValue[i]; bothNaN = (oldItem !== oldItem) && (newItem !== newItem); if (!bothNaN && (oldItem !== newItem)) { changeDetected++; oldValue[i] = newItem; } } // ... could be slow…
  7. Two optimization options • Use $watch(expr,  fn,  false) • Change

    the reference, i.e. creation of a new collection • Use $watchCollection(expr,  fn)   • Decrease the iterations (possibly to one)
  8. • Use $watch(expr,  fn,  false) • Change the reference, i.e.

    creation of a new collection • Use $watchCollection(expr,  fn)   • Decrease the iterations (possibly to one) Two optimization options
  9. – Wikipedia “In object-oriented and functional programming, an immutable object

    is an object whose state cannot be modified after it is created.”
  10. immutable-demo.js let list = Immutable.List([1, 2, 3]); let changed =

    list.push(4); console.log(changed === list); // false console.log(list); // [ 1, 2, 3 ] console.log(changed); // [ 1, 2, 3, 4 ]
  11. The benchmark… Main parameters: • Collection Type (immutable or standard)

    • $watch   • $watchCollection   • Collection Size between 5 and 10,000 • Bindings Count between 5 and 100
  12. iterates n times over all elements of the results vs

    iterates n times over the results’ references $watchCollection $watch(..,.., false)
  13. • Use $watch(expr,  fn,  false) • Change the reference, i.e.

    creation of a new collection • Use $watchCollection(expr,  fn)   • Decrease the iterations (possibly to one) Two optimization options
  14. Two optimization options • Use $watch(expr,  fn,  false) • Change

    the reference, i.e. creation of a new collection • Use $watchCollection(expr,  fn)   • Decrease the iterations (possibly to one)
  15. VersionableList function VersionableList(list) { 'use strict’; this._version = 0; defineProperty(this,

    '_data', { enumerable: false, value: list || [] }); } 'push pop shift unshift'.split(' ') .forEach(function (key) { 'use strict'; defineMethod(VersionableList.prototype, key, function () { this._data[key].apply(this._data, arguments); this._updateVersion(); }); });
  16. VersionableList function VersionableList(list) { 'use strict’; this._version = 0; defineProperty(this,

    '_data', { enumerable: false, value: list || [] }); } 'push pop shift unshift'.split(' ') .forEach(function (key) { 'use strict'; defineMethod(VersionableList.prototype, key, function () { this._data[key].apply(this._data, arguments); this._updateVersion(); }); });
  17. VersionableList function VersionableList(list) { 'use strict’; this._version = 0; defineProperty(this,

    '_data', { enumerable: false, value: list || [] }); } 'push pop shift unshift'.split(' ') .forEach(function (key) { 'use strict'; defineMethod(VersionableList.prototype, key, function () { this._data[key].apply(this._data, arguments); this._updateVersion(); }); });
  18. Usage Demo let list = new VersionableList([1, 2]); console.log(list.valueOf()); //

    [1, 2] console.log(list._version); // 0 list.push(3); console.log(list.valueOf()); // [1, 2, 3] console.log(list._version); // 1
  19. In Angular 2 • Unidirectional data flow • One-way data

    binding • No TTL (a single iteration of the $digest) • Less iterations over the watched expressions
  20. Component Input Output {      photoUrl:  “photo.png”,    

     firstName:  “Foo”,      lastName:  “Bar”,      age:  42
 } Perfect World Prof. Foo Bar 42 years old
  21. Component Input Output Prof. Foo Bar 42 years old Reality

    {      //  …
 } {      photoUrl:  “photo.png”,      firstName:  “Foo”,      lastName:  “Bar”,      age:  42
 }
  22. Component Input Output Reality Mutable state {      //

     …
 } {      photoUrl:  “photo.png”,      firstName:  “Foo”,      lastName:  “Bar”,      age:  42
 } Prof. Foo Bar 42 years old
  23. app todos todo todo user {      photoUrl:  “photo.png”,

         firstName:  “Foo”,      lastName:  “Bar”,      age:  42,      updated:  1429260707251,      todos:  [{          title:  “Save  the  Universe”,          updated:  1429260907251,          completed:  true      },  {          title:  “Write  tests”,          updated:  1429260917251,          completed:  false      }]
 }
  24. app todos todo todo user {      photoUrl:  “photo.png”,

         firstName:  “Foo”,      lastName:  “Bar”,      age:  42,      updated:  1429260707251,      todos:  [{          title:  “Save  the  Universe”,          updated:  1429260907251,          completed:  true      },  {          title:  “Write  tests”,          updated:  1429260917251,          completed:  false      }]
 }
  25. app todos todo todo user {      photoUrl:  “photo.png”,

         firstName:  “Foo”,      lastName:  “Bar”,      age:  42,      updated:  1429260707251,      todos:  [{          title:  “Save  the  Universe”,          updated:  1429260907251,          completed:  true      },  {          title:  “Write  tests”,          updated:  1429260917251,          completed:  false      }]
 }
  26. app todos todo todo user Prof. Foo Bar 42 years

    old 1 todo pending [x] Save the Universe [x] Write tests
  27. app todos todo todo user user.todos = user.todos.filter(t => !t.completed

    }); Change user.todos • todos will get wrong items • Angular needs to perform change detection over the whole tree
  28. app todos todo todo user user.todos = user.todos.filter(t => !t.completed

    }); Change user.todos • todos will get wrong items • Angular needs to perform change detection over the whole tree
  29. app todos todo todo user user.todos = user.todos.filter(t => !t.completed

    }); Change user.todos • todos will get wrong items • Angular needs to perform change detection over the whole tree
  30. Component Input Output Prof. Foo Bar 42 years old Reality

    Mutable state {      //  …
 } {      photoUrl:  “photo.png”,      firstName:  “Foo”,      lastName:  “Bar”,      age:  42
 }
  31. References • Boost the Performance of an AngularJS App Using

    Immutable Data - Part 1 • Boost the Performance of an AngularJS App Using Immutable Data - Part 2 • Even Faster AngularJS Data Structures • angular-immutable • Persistent Data Structures • Benchmarks • Versionable collections • Change Detection Reinvented • Change Detection in Angular 2 • Immutable.js