Run digest cycle in web worker

Dr. Gleb Bahmutov PhD
Kensho Boston / NYC
Personal site http://glebbahmutov.com/
Blog http://glebbahmutov.com/blog/

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);
});

function () {
scope.n += 2000;
scope.\$digest(function (scope) {
var html = computeMarkup(scope);
render(html);
});
});``````

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);
};``````

Kensho ♥ Angular

``````  main context (index.html)  |     worker.js
-----------------------------|---------------------------------------
client uses mockScopes    | actual scopes
loads worker.js           | \$digest returns markup text``````

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);
});

function () {
localN += 2000;
scope.set(n, localN);
scope.\$digest(function (scope) {
render(computeMarkup(scope));
});
});
``````

Kensho ♥ 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

``````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 '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/