Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Last active June 30, 2022 23:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfkaye/a469369646401208a2cb941cf7127756 to your computer and use it in GitHub Desktop.
Save dfkaye/a469369646401208a2cb941cf7127756 to your computer and use it in GitHub Desktop.
self-serialize a command interface as an embedded worker; examples with object.prototype and class inheritance
// 18 june 2022
// self-serialize to a worker
// 21 june 2022
// self-serialize a command interface as an embedded worker
// 30 June 2022
// - slight cosmetic changes to prototype version
// - class implementation added (below)
// The idea takes less than 25 words to describe, but is still an
// extremely sketchy one.
// Define an object that serializes itself as a worker, then any
// command object can extend it.
// This is the prototype based version which does **not** demonstrate inheritance.
function Test() {
return Test.create();
}
// commands
Test.prototype.pause = function (m) {
console.log(m, "pausing");
return "paused";
};
Test.prototype.resume = function (m) {
console.log(m, "resuming");
return "resumed";
};
// static factory
Test.create = function () {
console.log("create");
var base = Test.prototype;
var self = Object.create(base);
// map functions as strings
for (var k in self) {
if (typeof self[k] == "function") {
self[k] = self[k].toString();
}
}
// create worker script
var script = `
self.actions = ${ JSON.stringify(self) };
Object.keys(actions).forEach(function (k, F) {
F = Function("actions", "return " + actions[k] + ".bind(actions)");
actions[k] = F(actions);
});
self.onmessage = function (request) {
var action = request.data.action;
console.log("action:", action);
var response = action in actions && actions[action]("should " + action);
if (response) {
postMessage(response);
}
};
`;
// console.log( script )
// create blob
// create url
// create worker
var blob = new Blob([script], {type: 'text/javascript'});
var url = window.URL.createObjectURL(blob);
var worker = new Worker(url);
// return control { pause => worker.pause, resume => worker.pause }
var api = {
onmessage: function (response) {
console.log("done", response);
}
};
// map functions as commands to the worker
Object.keys(self).forEach(function (k) {
if (typeof base[k] == "function") {
api[k] = function() {
worker.postMessage({ action: k });
};
}
});
worker.onmessage = function (response) {
api.onmessage(response.data);
};
return api;
};
/* test it out */
var a = Test();
a.pause();
a.resume();
// create
// action: pause
// should pause pausing
// action: resume
// should resume resuming
// done paused
// done resumed
// 18 june 2022
// self-serialize to a worker
// 21 june 2022
// self-serialize a command interface as an embedded worker
// 30 June 2022
// - slight cosmetic changes to prototype version
// - class implementation added (below)
// The idea takes less than 25 words to describe, but is still an
// extremely sketchy one.
// Define an object that serializes itself as a worker, then any
// command object can extend it.
// This is the class based version which **does** demonstrate inheritance.
class Serialize {
constructor() {
return Serialize.create(this);
}
static create(self) {
console.log("create");
var base = self.constructor.prototype
// map functions as strings
// Two reasons classes are terrible:
// 1. class prototype methods are **not** for...in enumerable
Object.getOwnPropertyNames(base).forEach(k => {
if (typeof self[k] == "function" && k != "constructor") {
var fs = self[k].toString().trim();
// 2. prefix "function " to methods defined with shorthand (pause() {...})
self[k] = /^function/.test(fs)
? fs
: "function " + fs;
}
});
// create worker script
var script = `
self.actions = ${ JSON.stringify( self ) };
Object.keys(actions).forEach(function (k, F) {
F = Function("actions", "return " + actions[k] + ".bind(actions)");
actions[k] = F(actions);
});
self.onmessage = function (request) {
var action = request.data.action;
console.log("action:", action);
var response = action in actions && actions[action]("should " + action);
if (response) {
postMessage(response);
}
};
`;
// console.log( script )
// create blob
// create url
// create worker
var blob = new Blob([script], {type: 'text/javascript'});
var url = window.URL.createObjectURL(blob);
var worker = new Worker(url);
// return control { pause => worker.pause, resume => worker.pause }
var api = {
onmessage: function (response) {
console.log("done", response);
}
};
// map functions as commands to the worker
// assign action function to api if the key on self is a function on the prototype.
Object.keys(self).forEach(function (k) {
if (typeof base[k] == "function") {
api[k] = function () {
worker.postMessage({ action: k });
};
}
});
worker.onmessage = function (response) {
api.onmessage(response.data);
};
return api;
}
}
class Test extends Serialize {
constructor() {
return super();
}
pause(m) {
console.log(m, "pausing");
return "paused"
}
resume(m) {
console.log(m, "resuming");
return "resumed"
}
}
/** test it out **/
var w = new Test();
w.pause();
w.resume();
// create
// action: pause
// should pause pausing
// action: resume
// should resume resuming
// done paused
// done resumed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment