Skip to content

Instantly share code, notes, and snippets.

@sketchpunk
Last active February 6, 2023 22:51
Show Gist options
  • Save sketchpunk/9584e5fb82185691602cd3ea9b57f874 to your computer and use it in GitHub Desktop.
Save sketchpunk/9584e5fb82185691602cd3ea9b57f874 to your computer and use it in GitHub Desktop.
Various Web Worker Wrappers
/*
const fnWorker = ( evt )=>{
const data = evt.data;
fnCreateVert( data );
};
async function fnCreateVert( y ){
const vert = new Float32Array( [0,y,0] );
console.log( 'Elevation Request', y, vert );
console.log( 'Size before post', vert.byteLength );
//postMessage( vert ); // Copy to main thread
postMessage( vert, [ vert.buffer ] ); // Transfer to main thread
console.log( 'Size after post', vert.byteLength );
};
const dw = new DynamicWorker( [fnCreateVert, fnWorker] );
dw.on(( e )=>{
console.log( 'Drawing Vert', Array.from( e.data ) );
Ref.pnt.add( e.data, 0x00ff00, 5 );
});
dw.post( 1.2 );
*/
class DynamicWorker{
constructor( fn ){
if( !window.Worker ){ console.error( 'Browser does not support Web Workers' ); return; }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Build Source
let src = '';
if( Array.isArray( fn ) ){
// Turn all the other functions into strings
const li = fn.length - 1;
for( let i=0; i < li; i++ ) src += fn[ i ].toString() + '\n';
// Last function is the onMessage Handler
src += 'self.onmessage=' + fn[ li ].toString();
}else{
src = 'self.onmessage=' + fn.toString();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Compile source & create workier
const blob = new Blob( [ src ], { type:'text/javascript' } );
const url = window.URL.createObjectURL( blob );
window.URL.revokeObjectURL(blob);
//console.log( src );
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this._worker = new Worker( url, { type: 'module' } );
}
dispose(){ this._worker.terminate(); }
post( data ){ this._worker.postMessage( data ); return this; }
once( fn ){ this._worker.addEventListener( 'message', fn, { once:true } ); return this; }
on( fn ){ this._worker.addEventListener( 'message', fn ); return this; }
}
/*
let pworker = new PromiseWorker( v=>{
console.log( "Inside ", v );
return "Got " + v;
} );
pworker.post( "test" ).then( v=>console.log( "then", v ) );
*/
class PromiseWorker{
constructor( fn ){
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const blob = new Blob(
[`self.onmessage = (e)=>{
const rtn = __WORKER_FN__( e.data.value );
if( rtn != undefined ) self.postMessage( { id:e.data.id, value:rtn } );
}; `, 'const __WORKER_FN__=', fn.toString() ],
{ type:'text/javascript' }
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const url = window.URL.createObjectURL( blob );
window.URL.revokeObjectURL( blob );
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this._promises = new Map();
this._worker = new Worker( url );
this._worker.addEventListener( "message", this._onMsg.bind( this ) );
}
_onMsg( e ){
const p = this._promises.get( e.data.id );
if( p ){
this._promises.delete( e.data.id );
p.resolve( e.data.value );
}else{
console.warn( 'Worker Promise ID not found in map' );
}
}
_nanoId( t=21 ){
const r = crypto.getRandomValues( new Uint8Array( t ) );
let n, e = "";
for( ;t--; ){
n = 63 & r[ t ];
e += ( n < 36 )? n.toString(36) :
( n < 62 )? ( n - 26 ).toString( 36 ).toUpperCase() :
( n < 63 )? "_" : "-";
}
return e;
}
post( value, fnThen=null ){
const id = this._nanoId( 8 );
const p = new Promise(( resolve, reject )=>{
this._promises.set( id, {resolve,reject} );
this._worker.postMessage( { id, value } );
});
return ( fnThen )? p.then( fnThen ) : p;
}
}
/*
let runner = new TaskRunner();
runner.add( "BasicTask", BasicTask, e=>console.log( "Msg", e.data ) );
runner.post( "BasicTask", "woot" );
*/
class BasicTask{
run( e ){
console.log( "Run", e.data );
self.postMessage( e.data );
}
}
class TaskRunner{
_workers = new Map();
constructor(){}
_newWorker( src ){
const blob = new Blob( [ src ], { type:'text/javascript' } );
const url = window.URL.createObjectURL( blob );
window.URL.revokeObjectURL(blob);
return new Worker( url );
}
add( name, task, onMsg=null ){
let src = task.toString() + `
const TASK = new ${task.name}();
self.onmessage = e=>TASK.run( e );`;
let worker = this._newWorker( src );
if( onMsg ) worker.addEventListener( "message", onMsg );
this._workers.set( name, worker );
return this;
}
on( name, fn ){
const task = this._workers.get( name );
if( task ) task.addEventListener( "message", fn );
return this;
}
once( name, fn ){
const task = this._workers.get( name );
if( task ) task.addEventListener( "message", fn, { once:true } );
return this;
}
post( name, data ){
const task = this._workers.get( name );
if( task ) task.postMessage( data );
return this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment