Last active
June 8, 2022 04:36
-
-
Save dfkaye/8e10996623246d4b178f1d4a7939673d to your computer and use it in GitHub Desktop.
communicate with workers through message channels
This file contains 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
// 7 June 2022 | |
// communicate with workers through message channels | |
// Note: The following example won't run in browser pages with Content Security Policy | |
// that contains a worker-src directive that does not specify blob: as a value, | |
// such as a console in a GitHub gist (where I do lot of experimenting). | |
// If there is no worker-src attribute, then this should *just work*... | |
// The MessageChannel API lets us send messages between brower windows, tabs, | |
// iframes, and workers. | |
// We can set up an embedded worker to initialize itself with a message channel | |
// by sending a run-once message containing the channel and its receiving port | |
// as a transferable object. | |
// A transferable object is no longer accessible to the sender once it is sent | |
// and accepted by a receiver. | |
// We can still send messages to workers using the postMessage method, but we | |
// can now also send them by a message channel's port's postMessage method. | |
// I am: | |
// 1. skeptical that using both communication methods at once as it duplicates | |
// work. | |
// 2. skeptical of the benefits of communicating by a message channel versus | |
// communicating directly with a worker. | |
// 3. open to persuasion of the benefits of either of these doubts. | |
var script = ` | |
// A local response value. | |
var response = "Worker says:"; | |
// Handle message as a request. | |
self.onmessage = function (request) { | |
if (request.data == 'init') { | |
// Here's one reason *not* to use both worker.onmessage and port.onmessage. | |
// This handler has to ignore the initialization command meant for the channel's | |
// port setup. | |
console.log("**ignoring init request in worker**"); | |
return | |
} | |
var value = request.data; | |
var type = typeof value; | |
// Send response. | |
postMessage([response, "type of ", value, "is", type]) | |
} | |
// Listen for messages on MessageChannels | |
self.port2; | |
self.port2Message = "Worker received on port2:" | |
self.addEventListener('message', initPort); | |
// Setup the transferred port from the ports list. | |
function initPort(e) { | |
console.log("**running init port request in worker**"); | |
console.log(e.ports); | |
// self.port2 = e.ports[0]; | |
Object.defineProperty(self, 'port2', { | |
/* | |
* This is more than we need for this example, but this pattern lets us define | |
* the port as an immutable, but still-deletable property on the worker scope, | |
* in case we let the worker listen to bad actors by other messaging that try | |
* to overwrite the port's onmessage handler to send themselves all your private | |
* data. We'll test that hypothesis more thoroughly in a subsequent example... | |
*/ | |
value: e.ports[0], | |
writable: false, | |
enumerable: true, | |
configurable: true | |
}); | |
// Handle messages received on port2 | |
port2.onmessage = onPortMessage; | |
// Remove this function as a listener so we execute it only once. | |
self.removeEventListener('message', initPort) | |
} | |
// Handle messages received on port2 | |
function onPortMessage(e) { | |
port2.postMessage([port2Message, e.data]); | |
} | |
`; | |
var blob = new Blob([script], {type: 'text/javascript'}); | |
var url = window.URL.createObjectURL(blob); | |
var worker = new Worker(url); | |
var channel = new MessageChannel(); | |
var port1 = channel.port1; | |
// Handle messages received on port1. | |
port1.onmessage = function (e) { | |
console.warn("Received on port1: " + e.data.join(" ")); | |
}; | |
// Handle messages received from the worker. | |
worker.onmessage = function(response) { | |
console.log('Received from worker: ' + response.data.join(" ")); | |
}; | |
// Send command to initialize worker with transferable object (channel.port2). | |
worker.postMessage('init', [channel.port2]); | |
// Send messages to the channel. | |
port1.postMessage("**My Message**"); | |
port1.postMessage(33.33); | |
port1.postMessage(false); | |
port1.postMessage(null); | |
port1.postMessage(undefined); | |
port1.postMessage(6666666n); | |
port1.postMessage(/^abc$/gim); | |
port1.postMessage(new Date()); | |
port1.postMessage(new Map()); | |
// Send messages to the worker. | |
worker.postMessage("**My Message**"); | |
worker.postMessage(33.33); | |
worker.postMessage(false); | |
worker.postMessage(null); | |
worker.postMessage(undefined); | |
worker.postMessage(6666666n); | |
worker.postMessage(/^abc$/gim); | |
worker.postMessage(new Date()); | |
worker.postMessage(new Map()); | |
// Quite a message trace. | |
// In Firefox the messages are interleaved, while in Chrome and Edge, | |
// the worker messages appear first, then the channel messages. | |
// **ignoring init request in worker** | |
// **running init port request in worker** | |
// Array [ "[object MessagePort]" ] | |
// Received from worker: Worker says: type of **My Message** is string | |
// Received on port1: Worker received on port2: "**My Message**" | |
// Received from worker: Worker says: type of 33.33 is number | |
// Received on port1: Worker received on port2: "33.33" | |
// Received from worker: Worker says: type of false is boolean | |
// Received on port1: Worker received on port2: "false" | |
// Received from worker: Worker says: type of is object | |
// Received on port1: Worker received on port2: "null" | |
// Received from worker: Worker says: type of is undefined | |
// Received on port1: Worker received on port2: "undefined" | |
// Received from worker: Worker says: type of 6666666 is bigint | |
// Received on port1: Worker received on port2: "6666666" | |
// Received from worker: Worker says: type of /^abc$/gim is object | |
// Received on port1: Worker received on port2: "/^abc$/gim" | |
// Received from worker: Worker says: type of Tue Jun 07 2022 09:33:50 GMT-0700 (Pacific Daylight Time) is object | |
// Received on port1: Worker received on port2: "Tue Jun 07 2022 09:33:50 GMT-0700 (Pacific Daylight Time)" | |
// Received from worker: Worker says: type of [object Map] is object | |
// Received on port1: Worker received on port2: "[object Map]" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment