1
0

worker-util.js 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. 'use strict';
  2. /* exported createWorker */
  3. function createWorker({url, lifeTime = 300}) {
  4. let worker;
  5. let id;
  6. let timer;
  7. const pendingResponse = new Map();
  8. return new Proxy({}, {
  9. get(target, prop) {
  10. return (...args) => {
  11. if (!worker) init();
  12. return invoke(prop, args);
  13. };
  14. },
  15. });
  16. function init() {
  17. id = 0;
  18. worker = new Worker('/js/worker-util.js?' + new URLSearchParams({url}));
  19. worker.onmessage = onMessage;
  20. }
  21. function uninit() {
  22. worker.onmessage = null;
  23. worker.terminate();
  24. worker = null;
  25. }
  26. function onMessage({data: {id, data, error}}) {
  27. pendingResponse.get(id)[error ? 'reject' : 'resolve'](data);
  28. pendingResponse.delete(id);
  29. if (!pendingResponse.size && lifeTime >= 0) {
  30. timer = setTimeout(uninit, lifeTime * 1000);
  31. }
  32. }
  33. function invoke(action, args) {
  34. return new Promise((resolve, reject) => {
  35. pendingResponse.set(id, {resolve, reject});
  36. clearTimeout(timer);
  37. worker.postMessage({id, action, args});
  38. id++;
  39. });
  40. }
  41. }
  42. /* exported createWorkerApi */
  43. function createWorkerApi(methods) {
  44. self.onmessage = async ({data: {id, action, args}}) => {
  45. let data, error;
  46. try {
  47. data = await methods[action](...args);
  48. } catch (err) {
  49. error = true;
  50. data = cloneError(err);
  51. }
  52. self.postMessage({id, data, error});
  53. };
  54. }
  55. function cloneError(err) {
  56. return Object.assign({
  57. name: err.name,
  58. stack: err.stack,
  59. message: err.message,
  60. lineNumber: err.lineNumber,
  61. columnNumber: err.columnNumber,
  62. fileName: err.fileName,
  63. }, err);
  64. }
  65. if (self.WorkerGlobalScope) {
  66. const loadedUrls = [];
  67. self.require = urls => {
  68. const toLoad = (Array.isArray(urls) ? urls : [urls])
  69. .map(u => u.endsWith('.js') ? u : u + '.js')
  70. .filter(u => !loadedUrls.includes(u));
  71. if (toLoad) {
  72. loadedUrls.push(...toLoad);
  73. importScripts(...toLoad);
  74. }
  75. };
  76. const url = new URLSearchParams(location.search).get('url');
  77. if (url) require(url);
  78. }