Skip to content

Instantly share code, notes, and snippets.

@oakfang
Last active October 19, 2015 13:52
Show Gist options
  • Save oakfang/34a46f402f11987f1da7 to your computer and use it in GitHub Desktop.
Save oakfang/34a46f402f11987f1da7 to your computer and use it in GitHub Desktop.
WebWorker module
function functionToBlob(func) {
return new Blob([`(${func.toString()})()`]);
}
function importAsBlob(func) {
return new Blob([`var ${func.name} = ${func.toString()}`]);
}
function getFunctionUrl(func, imported) {
return URL.createObjectURL((imported ? importAsBlob : functionToBlob)(func));
}
function workerMain() {
this.onmessage = ({data}) => {
switch(data.type) {
case 'import':
importScripts(data.url);
this.postMessage({type: 'imported'});
break;
case 'execute':
var func = this[data.function];
var args = data.args;
try {
self.postMessage({
type: 'return',
value: func(...args)
});
} catch (err) {
self.postMessage({
type: 'error',
value: err.toString()
});
}
break;
}
};
}
function createWorker() {
var url = getFunctionUrl(workerMain);
return new Worker(url);
}
function thread(func, performanceChecker) {
if (!func.name) {
throw new Error('A thread has to have a name');
}
var executionResolver,
executionRejecter,
importResolver;
var worker = createWorker();
var importPromise = new Promise(resolver => {
importResolver = resolver;
});
worker.postMessage({
type: 'import',
url: getFunctionUrl(func, true)
});
worker.onmessage = function (e) {
switch(e.data.type) {
case 'error':
executionRejecter(e.data.value);
break;
case 'return':
executionResolver(e.data.value);
break;
case 'imported':
importResolver();
}
};
var thunk = function () {
var args = Array.from(arguments);
if (performanceChecker && !performanceChecker(...args)) {
console.log('Thread is too heavy for this... Using AsyncWrapper');
return getAsyncWrapper(func)(...args);
}
return importPromise.then(function () {
worker.postMessage({
type: 'execute',
function: func.name,
args: args
});
return new Promise((resolve, reject) => {
executionRejecter = reject;
executionResolver = resolve;
});
});
};
thunk.close = function () {
worker.terminate();
};
return thunk;
}
function getAsyncWrapper(func) {
var wrapper = function () {
var args = Array.from(arguments);
return new Promise((resolve, reject) => {
try {
resolve(func(...args));
} catch (err) {
reject(err);
}
})
};
wrapper.close = function () {};
return wrapper;
}
/**
@param {performanceChecker} if defined and resolves to false when passed the thunks arguments, will not start a new thread
*/
export default function (func, performanceChecker) {
if (window.Worker && window.URL && window.Blob) {
return thread(func, performanceChecker);
} else {
return getAsyncWrapper(func);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment