helper.js (3862B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* eslint-env amd */ 6 7 "use strict"; 8 9 /* global workerHelper */ 10 /* exported workerHelper */ 11 (function (root, factory) { 12 if (typeof define === "function" && define.amd) { 13 define(factory); 14 } else if (typeof exports === "object") { 15 module.exports = factory(); 16 } else { 17 root.workerHelper = factory(); 18 } 19 })(this, function () { 20 /** 21 * This file is to only be included by ChromeWorkers. This exposes 22 * a `createTask` function to workers to register tasks for communication 23 * back to `devtools/shared/worker`. 24 * 25 * Tasks can be send their responses via a return value, either a primitive 26 * or a promise. 27 * 28 * createTask(self, "average", function (data) { 29 * return data.reduce((sum, val) => sum + val, 0) / data.length; 30 * }); 31 * 32 * createTask(self, "average", function (data) { 33 * return new Promise((resolve, reject) => { 34 * resolve(data.reduce((sum, val) => sum + val, 0) / data.length); 35 * }); 36 * }); 37 * 38 * 39 * Errors: 40 * 41 * Returning an Error value, or if the returned promise is rejected, this 42 * propagates to the DevToolsWorker as a rejected promise. If an error is 43 * thrown in a synchronous function, that error is also propagated. 44 */ 45 46 /** 47 * Takes a worker's `self` object, a task name, and a function to 48 * be called when that task is called. The task is called with the 49 * passed in data as the first argument 50 * 51 * @param {object} self 52 * @param {string} name 53 * @param {function} fn 54 */ 55 function createTask(self, name, fn) { 56 // Store a hash of task name to function on the Worker 57 if (!self._tasks) { 58 self._tasks = {}; 59 } 60 61 // Create the onmessage handler if not yet created. 62 if (!self.onmessage) { 63 self.onmessage = createHandler(self); 64 } 65 66 // Store the task on the worker. 67 self._tasks[name] = fn; 68 } 69 70 /** 71 * Creates the `self.onmessage` handler for a Worker. 72 * 73 * @param {object} self 74 * @return {function} 75 */ 76 function createHandler(self) { 77 return function (e) { 78 const { id, task, data } = e.data; 79 const taskFn = self._tasks[task]; 80 81 if (!taskFn) { 82 self.postMessage({ id, error: `Task "${task}" not found in worker.` }); 83 return; 84 } 85 86 try { 87 handleResponse(taskFn(data)); 88 } catch (ex) { 89 handleError(ex); 90 } 91 92 function handleResponse(response) { 93 // If a promise 94 if (response && typeof response.then === "function") { 95 response.then( 96 val => self.postMessage({ id, response: val }), 97 handleError 98 ); 99 } else if (response instanceof Error) { 100 // If an error object 101 handleError(response); 102 } else { 103 // If anything else 104 self.postMessage({ id, response }); 105 } 106 } 107 108 function handleError(error = "Error") { 109 try { 110 // First, try and structured clone the error across directly. 111 self.postMessage({ id, error }); 112 } catch (x) { 113 // We could not clone whatever error value was given. Do our best to 114 // stringify it. 115 let errorString = `Error while performing task "${task}": `; 116 117 try { 118 errorString += error.toString(); 119 } catch (ex) { 120 errorString += "<could not stringify error>"; 121 } 122 123 if ("stack" in error) { 124 try { 125 errorString += "\n" + error.stack; 126 } catch (err) { 127 // Do nothing 128 } 129 } 130 131 self.postMessage({ id, error: errorString }); 132 } 133 } 134 }; 135 } 136 137 return { createTask }; 138 });