He estado tratando de usar Web Workers con reaccionar durante unos días y me encontré con algunos problemas.
Empecé con una aplicación create-react usando webpack 4. Podría usar un trabajador web usando este tutorial: https://javascript.plainenglish.io/web-worker-in-react-9b2efafe309c que carga un WebWorker de esa manera:
export default class WorkerBuilder extends Worker { constructor(worker) { const code = worker.toString(); const blob = new Blob([`(${code})()`]); return new Worker(URL.createObjectURL(blob)); } }Desafortunadamente, necesito usar una lib de mi trabajador (d3-delaunay) y el paquete web me dio un error cuando intenté hacerlo (creo que cambió su ruta).
Escuché sobre el cargador de trabajadores y me enteré de que esto había quedado obsoleto ya que la importación de Web Worker ahora está integrada en el paquete web 5.
Actualicé mi aplicación a WebPack 5 (como se explica en la wiki de crear-reaccionar-aplicación), esto no fue fácil ya que muchas de mis dependencias se rompieron.
Pero cuando traté de cargar mi WebWorker como se explica aquí: https://webpack.js.org/guides/web-workers/ esto no funcionó en absoluto (sin errores).
Ese es mi código:
console.log("hello"); const worker = new Worker(new URL('./world.worker.js', import.meta.url)); worker.postMessage({ question: 'The Answer to the Ultimate Question of Life, The Universe, and Everything.', }); worker.onmessage = ({ data: { answer } }) => { console.log(answer); }; console.log("world");Y mi world.worker.js:
import { Delaunay } from "d3-delaunay"; // eslint-disable-next-line import/no-anonymous-default-export export default () => { // eslint-disable-next-line no-restricted-globals self.onmessage = (message) => { console.log("yolo"); }; };Pero mi única salida fue:
hello worldMi trabajador no parecía funcionar.
Finalmente me di cuenta de esto, tenía el mismo problema. El mayor problema es que no debe usar la exportación predeterminada en el trabajador web. Así es como lo hice funcionar.
Debe crear una instancia del trabajador utilizando React.useMemo. Esto es para que solo se cree un trabajador.
const worker = React.useMemo(() => new Worker(new URL('./world.worker.js', import.meta.url)), []);Para obtener la respuesta, necesita saber cuándo se actualiza el objeto de trabajo. Para hacer esto, puede usar React.useEffect.
React.useEffect(() => { worker.onmessage = ({ data: { answer } }) => { console.log(answer); return () => worker.terminate(); } }, [worker]);Finalmente, su archivo world.worker.js tiene dos problemas. La primera es que en realidad no devuelve nada. Deberá agregar una llamada a postMessage. El segundo problema, que es el que tuve problemas para resolver, es que no debe envolver la función en el valor predeterminado de exportación. Creo que esto se debe a que webpack lo envolverá por ti. Consulte a continuación el código correcto de world.worker.js.
import { Delaunay } from "d3-delaunay"; self.onmessage = ({ data: { question } }) => { postMessage({ answer: 42, }); };para usar módulos es6 en trabajadores, debe cargarlo en modo script usando esta sintaxis:
new Worker('worker-path.js', {type: 'module'})