workway
Version:
A general purpose, Web Worker driven, remote namespace with classes and methods.
109 lines (108 loc) • 3.49 kB
JavaScript
function workway(file) {'use strict';
/*! (c) 2018 Andrea Giammarchi (ISC) */
return new Promise(function (res) {
function uid() { return ++i + Math.random(); }
var i = 0;
var channel = uid();
var messages = {};
var worker = typeof file === 'string' ? new Worker(file) : file;
worker.addEventListener('message', function (event) {
if (event.data.channel !== channel) return;
event.stopImmediatePropagation();
var namespace = event.data.namespace;
if (namespace) {
var Class = function (info) {
var path = info.path;
var methods = info.methods;
var statics = info.statics;
var wm = new WeakMap;
function RemoteClass() { wm.set(this, uid()); }
methods.forEach(function (method) {
RemoteClass.prototype[method] = function () {
return send({
args: slice.call(arguments),
path: path,
method: method,
object: {
id: wm.get(this),
value: this
}
});
};
});
statics.methods.forEach(function (method) {
RemoteClass[method] = function () {
return send({
args: slice.call(arguments),
path: path,
method: method
});
};
});
statics.values.forEach(function (pair) {
RemoteClass[pair[0]] = pair[1];
});
return RemoteClass;
};
var callback = function (path) {
return function remoteCallback() {
return send({
args: slice.call(arguments),
path: path
});
};
};
var send = function (message) {
var resolve, reject;
var promise = new Promise(function (res, rej) {
resolve = res;
reject = rej;
});
promise.resolve = resolve;
promise.reject = reject;
messages[message.id = uid()] = promise;
worker.postMessage({
channel: channel,
message: message
});
return promise;
};
var slice = [].slice;
(function update(namespace) {
Object.keys(namespace).forEach(function (key) {
var info = namespace[key];
switch (info.type) {
case 'class': namespace[key] = Class(info); break;
case 'function': namespace[key] = callback(info.path); break;
case 'object': update(namespace[key] = info.value); break;
default: namespace[key] = info.value;
}
});
}(namespace));
res({
worker: worker,
namespace: namespace
});
} else {
var message = event.data.message;
var id = message.id;
var promise = messages[id];
delete messages[id];
if (message.hasOwnProperty('error')) {
var error, facade = message.error;
if (facade.hasOwnProperty('source'))
error = facade.source;
else {
error = new Error(facade.message);
error.stack = facade.stack;
}
promise.reject(error);
}
else
promise.resolve(message.result);
}
});
worker.postMessage({channel: channel});
});
}
module.exports = workway;