Gleb Bahmutov PRO
JavaScript ninja, image processing expert, software quality fanatic
Dr. Gleb Bahmutov PhD
Kensho Boston / NYC
Personal site http://glebbahmutov.com/
Blog http://glebbahmutov.com/blog/
Twitter @bahmutov
Kensho ♥ Angular
Kensho ♥ Angular
function findPrimes(n) {
var k, primes = [];
for (k = 0; k < n; k += 1) {
var prime = findPrime(k + 2);
primes.push(prime);
}
return primes;
}
Kensho ♥ Angular
// page code
var scope = new Scope();
scope.n = 0;
scope.$watch(function watchN(scope) {
return scope.n;
}, function computePrimes(newVal, oldVal, scope) {
if (newVal === oldVal) { return; }
scope.primes = findPrimes(scope.n);
});
document.getElementById('compute').addEventListener('click',
function () {
scope.n += 2000;
scope.$digest(function (scope) {
var html = computeMarkup(scope);
render(html);
});
});
Kensho ♥ Angular
Kensho ♥ Angular
Kensho ♥ Angular
// micro-angular.js
function Scope() {
this.$$watchers = [];
}
Scope.prototype.$watch = function(watchFn, listenerFn) {
var watcher = {
watchFn: watchFn,
listenerFn: listenerFn || function() { }
};
this.$$watchers.push(watcher);
};
Scope.prototype.$digest = function(cb) {
var self = this;
var dirty = this.$$watchers.some(function (watch) {
var newValue = watch.watchFn(self);
var oldValue = watch.last;
if (newValue !== oldValue) {
watch.listenerFn(newValue, oldValue, self);
watch.last = newValue;
return true;
}
});
dirty && cb && cb(this);
};
Source: Make your own Angular by Tero Parviainen
Kensho ♥ Angular
main context (index.html) | worker.js
-----------------------------|---------------------------------------
loads mock-scopes.js | loads micro-angular.js and primes.js
client uses mockScopes | actual scopes
loads worker.js | $digest returns markup text
Kensho ♥ Angular
Kensho ♥ Angular
// index.html app code
var scope = new Scope();
var localN = 0;
scope.set('n', 0);
scope.$watch(function (scope) {
return scope.n;
}, function (newVal, oldVal, scope) {
if (newVal === oldVal) { return; }
scope.primes = findPrimes(scope.n);
});
document.getElementById('compute').addEventListener('click',
function () {
localN += 2000;
scope.set(n, localN);
scope.$digest(function (scope) {
render(computeMarkup(scope));
});
});
Kensho ♥ Angular
Kensho ♥ Angular
function Scope() {
this.id = '$' + scopes;
scopes += 1;
digestWorker.postMessage({
cmd: 'Scope',
id: this.id
});
console.log('created mock scope', this.id);
}
Scope.prototype.set = function (name, value) {
digestWorker.postMessage({
cmd: 'set',
id: this.id,
name: name,
value: value
});
console.log('set mock scope', this.id, 'property', name, '=', value);
};
Scope.prototype.$watch = function (watchFn, listenerFn) {
digestWorker.postMessage({
cmd: '$watch',
id: this.id,
watchFn: watchFn.toString(),
listenerFn: listenerFn && listenerFn.toString()
});
};
Scope.prototype.$digest = function ($compile) { ... };
Kensho ♥ Angular
importScripts('micro-angular.js', 'primes.js');
var scopes = {};
onmessage = function digestOnMessage(e) {
switch (e.data.cmd) {
case 'Scope':
scopes[e.data.id] = new Scope(e.data.id);
break;
case 'set':
scopes[e.data.id][e.data.name] = e.data.value;
break;
case '$watch':
...
break;
case '$digest':
scopes[e.data.id].$digest(function digestFinished() {
var compiled = eval('(' + e.data.$compile + ')');
var scope = scopes[e.data.id];
var html = compiled(scope);
postMessage({
cmd: 'digestFinished',
html: html
});
});
break;
}
};
Kensho ♥ Angular
importScripts('micro-angular.js', 'primes.js');
var scopes = {};
onmessage = function digestOnMessage(e) {
switch (e.data.cmd) {
case 'Scope':
scopes[e.data.id] = new Scope(e.data.id);
break;
case 'set':
scopes[e.data.id][e.data.name] = e.data.value;
break;
case '$watch':
...
break;
case '$digest':
scopes[e.data.id].$digest(function digestFinished() {
var compiled = eval('(' + e.data.$compile + ')');
var scope = scopes[e.data.id];
var html = compiled(scope);
var diff = virtualDomDiffs(prevDom, newDom);
postMessage({
cmd: 'digestFinished',
diff: diff
});
});
break;
}
};
Kensho ♥ Angular
// BAD
scope.set('n', 0);
// GOOD
scope.n = 0;
// mock scope client-side
function Scope() {
digestWorker.postMessage({
cmd: 'Scope',
id: this.id
});
var self = this;
Object.observe(this, function (changes) {
changes.forEach(function (change) {
switch (change.type) {
case 'add':
case 'update':
console.log('change', change.name, 'to', change.object[change.name]);
self.set(change.name, change.object[change.name]);
break;
}
});
});
}
Kensho ♥ Angular
Dr. Gleb Bahmutov PhD
Kensho Boston / NYC
Personal site http://glebbahmutov.com/
Blog http://glebbahmutov.com/blog/
Twitter @bahmutov
Kensho ♥ Angular
By Gleb Bahmutov
An experiment in offloading the Angular dirty checking and model update step to a separate browser thread. ng-conf 2015, Salt Lake City, Utah. Video at http://youtu.be/lceLw8ROUP8?list=PLOETEcp3DkCoNnlhE-7fovYvqwVPrRiY7
JavaScript ninja, image processing expert, software quality fanatic