Last active
November 10, 2024 15:52
-
-
Save getify/1b26accb1a09aa53ad25 to your computer and use it in GitHub Desktop.
first draft sketch of a "Worker" polyfill
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Worker Polyfill</title> | |
<script src="polyfill.worker.js"></script> | |
</head> | |
<body> | |
<h1>Worker Polyfill</h1> | |
<script> | |
var w = new Worker("child.js"); | |
w.addEventListener("message",function(evt){ | |
console.log("from test.html:",evt.data); | |
}); | |
w.postMessage("hello world"); | |
// from child.js: hello world | |
// from test.html: HELLO WORLD | |
</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// child.js | |
var self = this; | |
addEventListener("message",function(evt){ | |
console.log("from child.js:",evt.data); | |
// here `self.` is optional | |
self.postMessage(evt.data.toUpperCase()); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// polyfill.worker.js | |
(function WorkerPolyfill(){ | |
function DualEventers() { | |
var cycle, scheduling_queue, | |
timer = (typeof setImmediate !== "undefined") ? | |
function timer(fn) { return setImmediate(fn); } : | |
setTimeout | |
; | |
// Note: using a queue instead of array for efficiency | |
function Queue() { | |
var first, last, item; | |
function Item(fn) { | |
this.fn = fn; | |
this.next = void 0; | |
} | |
return { | |
add: function add(fn) { | |
item = new Item(fn); | |
if (last) { | |
last.next = item; | |
} | |
else { | |
first = item; | |
} | |
last = item; | |
item = void 0; | |
}, | |
drain: function drain() { | |
var f = first; | |
first = last = cycle = null; | |
while (f) { | |
f.fn(); | |
f = f.next; | |
} | |
} | |
}; | |
} | |
scheduling_queue = Queue(); | |
function schedule(fn) { | |
scheduling_queue.add(fn); | |
if (!cycle) { | |
cycle = timer(scheduling_queue.drain); | |
} | |
} | |
function Eventer(handlers) { | |
function addEventListener(evtname,handler) { | |
if (handlers.local) { | |
handlers.local[evtname] = handlers.local[evtname] || []; | |
handlers.local[evtname].push(handler); | |
} | |
} | |
function notifyRemote(evtname,data) { | |
if (evtname != "error") { | |
data = { data: data }; | |
} | |
if (typeof handlers.remoteEventer["on" + evtname] == "function") { | |
handlers.remoteEventer["on" + evtname](data); | |
} | |
handlers.remote[evtname].forEach(function foreach(h){ | |
h(data); | |
}); | |
} | |
function postMessage(data) { | |
if (buffer) { | |
buffer.push(data); | |
return; | |
} | |
schedule(function scheduler(){ | |
notifyRemote("message",data); | |
}); | |
} | |
function unbuffer() { | |
if (buffer && buffer.length > 0) { | |
buffer.forEach(function foreach(item){ | |
notifyRemote("message",item); | |
}); | |
} | |
buffer = null; | |
} | |
var buffer = []; | |
this.onmessage = null; | |
this.onerror = null; | |
this.addEventListener = addEventListener; | |
this.postMessage = postMessage; | |
this.unbuffer = unbuffer; | |
this.notifyRemote = notifyRemote; | |
} | |
var e1, e2, | |
h1 = { | |
local: { message: [], error: [] } | |
}, | |
h2 = { | |
local: { message: [], error: [] } | |
} | |
; | |
// wire the duals' handlers together | |
h1.remote = h2.local; | |
h2.remote = h1.local; | |
// create the dual eventers | |
e1 = new Eventer(h1); | |
e2 = new Eventer(h2); | |
h1.remoteEventer = e2; | |
h2.remoteEventer = e1; | |
return [e1,e2]; | |
} | |
function importScripts() { | |
throw "importScripts() not supported by this polyfill."; | |
} | |
function publicEventerAPI(e) { | |
this.onmessage = e.onmessage; | |
this.onerror = e.onerror; | |
this.addEventListener = e.addEventListener; | |
this.postMessage = e.postMessage; | |
} | |
if (!window.Worker) { | |
window.Worker = function window$Worker(url) { | |
var eventers = DualEventers(), e1 = eventers[0], | |
e2 = eventers[1], worker, xhr, | |
unbufferE1 = e1.unbuffer, unbufferE2 = e2.unbuffer, | |
notifyE2 = e1.notifyRemote, notifyE1 = e2.notifyRemote, | |
globalPropNames = [], globalProps | |
; | |
// clean up public APIs | |
publicEventerAPI.call(this,e1); | |
e2 = new publicEventerAPI(e2); | |
// setup fake `self` global properties | |
e2.importScripts = importScripts; | |
e2.self = e2; | |
e2.navigator = window.navigator; | |
e2.location = { href: url }; | |
e2.XMLHttpRequest = window.XMLHttpRequest; | |
e2.WebSocket = window.WebSocket; | |
// prepare for worker fake Function | |
globalPropNames = Object.getOwnPropertyNames(e2); | |
globalProps = globalPropNames.map(function map(name){ | |
return e2[name]; | |
}); | |
// inside eventer doesn't need to be buffered | |
unbufferE2(); | |
// load the worker file | |
xhr = new XMLHttpRequest(); | |
xhr.onreadystatechange = function onready() { | |
if (xhr.readyState == 4) { | |
worker = Function.apply( | |
null, | |
globalPropNames.concat([xhr.responseText]) | |
); | |
try { | |
worker.apply(e2,globalProps); | |
// unbuffer outside eventer now | |
unbufferE1(); | |
} | |
catch (err) { | |
notifyE1("error",err); | |
} | |
} | |
}; | |
xhr.open( "GET", url ); | |
xhr.send(); | |
}; | |
} | |
})(); |
RyanCCollins
commented
Oct 20, 2016
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment