Run digest cycle in web worker
Dr. Gleb Bahmutov PhD
Kensho Boston / NYC
Personal site http://glebbahmutov.com/
Blog http://glebbahmutov.com/blog/
Twitter @bahmutov
Kensho ♥ Angular

Let us compute primes
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);
});
});Compute!
Kensho ♥ Angular
Coffee scripting
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 textMove model to a web worker
Digest takes too long?
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
Web worker micro angular

Kensho ♥ Angular
Client-side mock scopes
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
Worker adapter
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
Could we?
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
Restore POJO
// 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
Restored POJO working
Dr. Gleb Bahmutov PhD
Kensho Boston / NYC
Personal site http://glebbahmutov.com/
Blog http://glebbahmutov.com/blog/
Twitter @bahmutov
Kensho ♥ Angular

- this presentation at slides.com/bahmutov/run-digest-cycle-in-web-worker
- this presentation as a blog post glebbahmutov.com/blog/run-angular-digest-cycle-in-web-worker/
- Code at bahmutov/web-worker-digest-demo
Copy of Run digest cycle in web worker
By Praveen Poonia
Copy of Run digest cycle in web worker
An experiment in offloading the Angular dirty checking and model update step to a separate browser thread. ng-conf 2015, Salt Lake City, Utah
- 1,001