helper.js 3.7 KB

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