Administrar tareas de ejecución prolongada en una aplicación React con trabajadores web

Publicado: 2022-03-10
Resumen rápido ↬ En este tutorial, aprenderemos a usar la API de Web Worker para administrar tareas que consumen mucho tiempo y que bloquean la interfaz de usuario en una aplicación de JavaScript mediante la creación de una aplicación web de muestra que aprovecha Web Workers. Finalmente, terminaremos el artículo transfiriendo todo a una aplicación React.

El tiempo de respuesta es un gran problema cuando se trata de aplicaciones web. Los usuarios exigen respuestas instantáneas, sin importar lo que esté haciendo su aplicación. Ya sea que solo se muestre el nombre de una persona o se trate de calcular números, los usuarios de aplicaciones web exigen que su aplicación responda a sus comandos en todo momento. A veces, eso puede ser difícil de lograr dada la naturaleza de subproceso único de JavaScript. Pero en este artículo, aprenderemos cómo podemos aprovechar la API de Web Worker para brindar una mejor experiencia.

Al escribir este artículo, hice las siguientes suposiciones:

  1. Para poder seguir, debe tener al menos cierta familiaridad con JavaScript y la API del documento;
  2. También debe tener un conocimiento práctico de React para que pueda iniciar con éxito un nuevo proyecto de React utilizando Create React App.

Si necesita más información sobre este tema, he incluido una serie de enlaces en la sección "Recursos adicionales" para ayudarlo a ponerse al día.

Primero, comencemos con Web Workers.

¿Qué es un trabajador web?

Para comprender los trabajadores web y el problema que deben resolver, es necesario comprender cómo se ejecuta el código JavaScript en tiempo de ejecución. Durante el tiempo de ejecución, el código JavaScript se ejecuta secuencialmente y paso a paso. Una vez que finaliza un fragmento de código, comienza a ejecutarse el siguiente en la línea, y así sucesivamente. En términos técnicos, decimos que JavaScript es de un solo subproceso. Este comportamiento implica que una vez que comienza a ejecutarse algún fragmento de código, cada código que viene después debe esperar a que ese código termine de ejecutarse. Por lo tanto, cada línea de código "bloquea" la ejecución de todo lo que viene después. Por lo tanto, es deseable que cada pieza de código finalice lo más rápido posible. Si algún fragmento de código tarda demasiado en finalizar, parecería que nuestro programa ha dejado de funcionar. En el navegador, esto se manifiesta como una página congelada que no responde. En algunos casos extremos, la pestaña se congelará por completo.

Imagina conducir en un solo carril. Si alguno de los conductores delante de usted deja de moverse por algún motivo, entonces tiene un atasco de tráfico. Con un programa como Java, el tráfico podría continuar en otros carriles. Por lo tanto, se dice que Java es multihilo. Web Workers es un intento de llevar el comportamiento de subprocesos múltiples a JavaScript.

La siguiente captura de pantalla muestra que la API de Web Worker es compatible con muchos navegadores, por lo que debe sentirse seguro al usarla.

Mostrando gráfico de soporte de navegador para trabajadores web
Compatibilidad con navegador Web Workers. (Vista previa grande)

Los trabajadores web se ejecutan en subprocesos en segundo plano sin interferir con la interfaz de usuario y se comunican con el código que los creó a través de controladores de eventos.

Una excelente definición de Web Worker proviene de MDN:

"Un trabajador es un objeto creado usando un constructor (por ejemplo Worker() que ejecuta un archivo JavaScript con nombre; este archivo contiene el código que se ejecutará en el subproceso del trabajador; los trabajadores se ejecutan en otro contexto global que es diferente de la window actual. Por lo tanto, , usando el acceso directo de la window para obtener el alcance global actual (en lugar de self dentro de un Worker , devolverá un error).

Un trabajador se crea usando el constructor Worker .

 const worker = new Worker('worker-file.js')

Es posible ejecutar la mayoría del código dentro de un trabajador web, con algunas excepciones. Por ejemplo, no puede manipular el DOM desde dentro de un trabajador. No hay acceso a la API del document .

Los trabajadores y el hilo que los genera se envían mensajes entre sí mediante el método postMessage() . Del mismo modo, responden a los mensajes mediante el controlador de eventos onmessage . Es importante obtener esta diferencia. El envío de mensajes se logra mediante un método; recibir un mensaje de vuelta requiere un controlador de eventos. El mensaje que se recibe está contenido en el atributo de data del evento. Veremos un ejemplo de esto en la siguiente sección. Pero permítanme mencionar rápidamente que el tipo de trabajador del que hemos estado hablando se llama “trabajador dedicado”. Esto significa que el trabajador solo es accesible para el script que lo llamó. También es posible tener un trabajador al que se pueda acceder desde varios scripts. Estos se denominan trabajadores compartidos y se crean mediante el constructor SharedWorker , como se muestra a continuación.

 const sWorker = new SharedWorker('shared-worker-file.js')

Para obtener más información sobre los trabajadores, consulte este artículo de MDN. El propósito de este artículo es ayudarlo a comenzar a usar trabajadores web. Lleguemos a eso calculando el n-ésimo número de Fibonacci.

¡Más después del salto! Continúe leyendo a continuación ↓

Cálculo del enésimo número de Fibonacci

Nota: Para esta y las siguientes dos secciones, estoy usando Live Server en VSCode para ejecutar la aplicación. Seguro que puedes usar otra cosa.

Esta es la sección que estabas esperando. Finalmente escribiremos algo de código para ver Web Workers en acción. Bueno, no tan rápido. No apreciaríamos el trabajo que hace un Web Worker a menos que nos encontremos con el tipo de problemas que resuelve. En esta sección, veremos un problema de ejemplo y, en la siguiente sección, veremos cómo un trabajador web nos ayuda a hacerlo mejor.

Imagina que estuvieras creando una aplicación web que permitiera a los usuarios calcular el n-ésimo número de Fibonacci. En caso de que sea nuevo en el término 'número de Fibonacci', puede leer más sobre él aquí, pero en resumen, los números de Fibonacci son una secuencia de números en la que cada número es la suma de los dos números anteriores.

Matemáticamente se expresa como:

Así, los primeros números de la secuencia son:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ...

En algunas fuentes, la secuencia comienza en F 0 = 0 , en cuyo caso la siguiente fórmula se cumple para n > 1 :

En este artículo, comenzaremos en F 1 = 1. Una cosa que podemos ver de inmediato en la fórmula es que los números siguen un patrón recursivo. La tarea en cuestión ahora es escribir una función recursiva para calcular el n-ésimo número de Fibonacci (FN).

Después de algunos intentos, creo que puede encontrar fácilmente la función a continuación.

 const fib = n => { if (n < 2) { return n // or 1 } else { return fib(n - 1) + fib(n - 2) } }

La función es sencilla. Si n es menor que 2, devuelva n (o 1), de lo contrario, devuelva la suma de los FN n-1 y n-2 . Con funciones de flecha y operador ternario, podemos llegar a una sola línea.

 const fib = n => (n < 2 ? n : fib(n-1) + fib(n-2))

Esta función tiene una complejidad temporal de 0(2 n ) . Esto simplemente significa que a medida que aumenta el valor de n, el tiempo requerido para calcular la suma aumenta exponencialmente. Esto lo convierte en una tarea de ejecución muy prolongada que podría interferir potencialmente con nuestra interfaz de usuario, para valores grandes de n. Veamos esto en acción.

Nota : Esta no es la mejor manera de resolver este problema en particular. Mi elección de usar este método es para el propósito de este artículo.

Para comenzar, cree una nueva carpeta y asígnele el nombre que desee. Ahora dentro de esa carpeta crea una carpeta src/ . Además, cree un archivo index.html en la carpeta raíz. Dentro de la carpeta src/ , cree un archivo llamado index.js .

Abra index.html y agregue el siguiente código HTML.

 <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="styles.css"> </head> <body> <div class="heading-container"> <h1>Computing the nth Fibonnaci number</h1> </div> <div class="body-container"> <p id='error' class="error"></p> <div class="input-div"> <input id='number-input' class="number-input" type='number' placeholder="Enter a number" /> <button id='submit-btn' class="btn-submit">Calculate</button> </div> <div id='results-container' class="results"></div> </div> <script src="/src/index.js"></script> </body> </html>

Esta parte es muy simple. Primero, tenemos un encabezado. Entonces tenemos un contenedor con una entrada y un botón. Un usuario ingresaría un número y luego haría clic en "Calcular". También tenemos un contenedor para guardar el resultado del cálculo. Por último, incluimos el archivo src/index.js en una etiqueta de secuencia de script .

Puede eliminar el enlace de la hoja de estilo. Pero si tiene poco tiempo, he definido algunos CSS que puede usar. Simplemente cree el archivo styles.css en la carpeta raíz y agregue los estilos a continuación:

 body { margin: 0; padding: 0; box-sizing: border-box; } .body-container, .heading-container { padding: 0 20px; } .heading-container { padding: 20px; color: white; background: #7a84dd; } .heading-container > h1 { margin: 0; } .body-container { width: 50% } .input-div { margin-top: 15px; margin-bottom: 15px; display: flex; align-items: center; } .results { width: 50vw; } .results>p { font-size: 24px; } .result-div { padding: 5px 10px; border-radius: 5px; margin: 10px 0; background-color: #e09bb7; } .result-div p { margin: 5px; } span.bold { font-weight: bold; } input { font-size: 25px; } p.error { color: red; } .number-input { padding: 7.5px 10px; } .btn-submit { padding: 10px; border-radius: 5px; border: none; background: #07f; font-size: 24px; color: white; cursor: pointer; margin: 0 10px; }

Ahora abre src/index.js , vamos a desarrollarlo lentamente. Agregue el código a continuación.

 const fib = (n) => (n < 2 ? n : fib(n - 1) + fib(n - 2)); const ordinal_suffix = (num) => { // 1st, 2nd, 3rd, 4th, etc. const j = num % 10; const k = num % 100; switch (true) { case j === 1 && k !== 11: return num + "st"; case j === 2 && k !== 12: return num + "nd"; case j === 3 && k !== 13: return num + "rd"; default: return num + "th"; } }; const textCont = (n, fibNum, time) => { const nth = ordinal_suffix(n); return ` <p id='timer'>Time: <span class='bold'>${time} ms</span></p> <p><span class="bold" id='nth'>${nth}</span> fibonnaci number: <span class="bold" id='sum'>${fibNum}</span></p> `; };

Aquí tenemos tres funciones. La primera es la función que vimos anteriormente para calcular el n-ésimo FN. La segunda función es solo una función de utilidad para adjuntar un sufijo apropiado a un número entero. La tercera función toma algunos argumentos y genera un marcado que luego insertaremos en el DOM. El primer argumento es el número cuyo FN se está calculando. El segundo argumento es el FN calculado. El último argumento es el tiempo que lleva realizar el cálculo.

Todavía en src/index.js , agregue el siguiente código justo debajo del anterior.

 const errPar = document.getElementById("error"); const btn = document.getElementById("submit-btn"); const input = document.getElementById("number-input"); const resultsContainer = document.getElementById("results-container"); btn.addEventListener("click", (e) => { errPar.textContent = ''; const num = window.Number(input.value); if (num < 2) { errPar.textContent = "Please enter a number greater than 2"; return; } const startTime = new Date().getTime(); const sum = fib(num); const time = new Date().getTime() - startTime; const resultDiv = document.createElement("div"); resultDiv.innerHTML = textCont(num, sum, time); resultDiv.className = "result-div"; resultsContainer.appendChild(resultDiv); });

Primero, usamos la API de document para obtener los nodos DOM en nuestro archivo HTML. Obtenemos una referencia al párrafo donde mostraremos mensajes de error; la entrada; el botón de calcular y el contenedor donde mostraremos nuestros resultados.

A continuación, adjuntamos un controlador de eventos de "clic" al botón. Cuando se hace clic en el botón, tomamos lo que esté dentro del elemento de entrada y lo convertimos en un número, si obtenemos menos de 2, mostramos un mensaje de error y volvemos. Si obtenemos un número mayor que 2, continuamos. Primero, registramos la hora actual. Después de eso, calculamos el FN. Cuando eso termina, obtenemos una diferencia de tiempo que representa cuánto tiempo tomó el cálculo. En la parte restante del código, creamos un nuevo div . Luego configuramos su HTML interno para que sea la salida de la función textCont() que definimos anteriormente. Finalmente, le agregamos una clase (para darle estilo) y la agregamos al contenedor de resultados. El efecto de esto es que cada cálculo aparecerá en un div separado debajo del anterior.

Mostrando números de Fibonacci calculados hasta 43
Algunos números de Fibonacci. (Vista previa grande)

Podemos ver que a medida que aumenta el número, el tiempo de cálculo también aumenta (exponencialmente). Por ejemplo, de 30 a 35, el tiempo de cálculo saltó de 13 ms a 130 ms. Todavía podemos considerar que esas operaciones son "rápidas". A los 40 vemos un tiempo de cálculo de más de 1 segundo. En mi máquina, aquí es donde empiezo a notar que la página deja de responder. En este punto, ya no puedo interactuar con la página mientras se realiza el cálculo. No puedo concentrarme en la entrada ni hacer nada más.

¿Recuerdas cuando hablamos de que JavaScript es de un solo subproceso? Bueno, ese hilo ha sido "bloqueado" por este cálculo de larga duración, por lo que todo lo demás debe "esperar" a que finalice. Puede comenzar con un valor más bajo o más alto en su máquina, pero seguramente llegará a ese punto. Tenga en cuenta que tomó casi 10 segundos calcular eso de 44. Si hubiera otras cosas que hacer en su aplicación web, bueno, el usuario tiene que esperar a que Fib(44) termine antes de poder continuar. Pero si implementó un trabajador web para manejar ese cálculo, sus usuarios podrían continuar con otra cosa mientras se ejecuta.

Veamos ahora cómo los trabajadores web nos ayudan a superar este problema.

Un ejemplo de trabajador web en acción

En esta sección, delegaremos el trabajo de calcular el FN enésimo a un trabajador web. Esto ayudará a liberar el hilo principal y a mantener nuestra interfaz de usuario receptiva mientras se realiza el cálculo.

Comenzar con los trabajadores web es sorprendentemente simple. Veamos cómo. Cree un nuevo archivo src/fib-worker.js . e ingrese el siguiente código.

 const fib = (n) => (n < 2 ? n : fib(n - 1) + fib(n - 2)); onmessage = (e) => { const { num } = e.data; const startTime = new Date().getTime(); const fibNum = fib(num); postMessage({ fibNum, time: new Date().getTime() - startTime, }); };

Observe que hemos movido la función que calcula el n-ésimo número de Fibonacci, fib dentro de este archivo. Este archivo será ejecutado por nuestro trabajador web.

Recuerde que en la sección ¿Qué es un trabajador web ?, mencionamos que los trabajadores web y sus padres se comunican mediante el controlador de eventos onmessage y el método postMessage() . Aquí estamos usando el controlador de eventos onmessage para escuchar los mensajes del script principal. Una vez que recibimos un mensaje, desestructuramos el número del atributo de datos del evento. A continuación, obtenemos la hora actual y comenzamos el cálculo. Una vez que el resultado está listo, usamos el método postMessage() para publicar los resultados en el script principal.

Abra src/index.js , hagamos algunos cambios.

 ... const worker = new window.Worker("src/fib-worker.js"); btn.addEventListener("click", (e) => { errPar.textContent = ""; const num = window.Number(input.value); if (num < 2) { errPar.textContent = "Please enter a number greater than 2"; return; } worker.postMessage({ num }); worker.onerror = (err) => err; worker.onmessage = (e) => { const { time, fibNum } = e.data; const resultDiv = document.createElement("div"); resultDiv.innerHTML = textCont(num, fibNum, time); resultDiv.className = "result-div"; resultsContainer.appendChild(resultDiv); }; });

Lo primero que debe hacer es crear el trabajador web utilizando el constructor Worker . Luego, dentro del detector de eventos de nuestro botón, enviamos un número al trabajador usando worker.postMessage({ num }) . Después de eso, configuramos una función para detectar errores en el trabajador. Aquí simplemente devolvemos el error. Ciertamente puedes hacer más si quieres, como mostrarlo en DOM. A continuación, escuchamos los mensajes del trabajador. Una vez que recibimos un mensaje, desestructuramos time y fibNum , y continuamos el proceso de mostrarlos en el DOM.

Tenga en cuenta que dentro del trabajador web, el evento onmessage está disponible en el alcance del trabajador, por lo que podríamos haberlo escrito como self.onmessage y self.postMessage() . Pero en el script principal, tenemos que adjuntarlos al propio trabajador.

En la captura de pantalla a continuación, verá el archivo del trabajador web en la pestaña de fuentes de Chrome Dev Tools. Lo que debe notar es que la interfaz de usuario permanece receptiva sin importar el número que ingrese. Este comportamiento es la magia de los trabajadores web.

Vista de un archivo web worker activo
Un archivo de trabajador web en ejecución. (Vista previa grande)

Hemos avanzado mucho con nuestra aplicación web. Pero hay algo más que podemos hacer para mejorarlo. Nuestra implementación actual utiliza un solo trabajador para manejar cada cálculo. Si aparece un mensaje nuevo mientras se está ejecutando uno, se reemplaza el anterior. Para evitar esto, podemos crear un nuevo trabajador para cada llamada para calcular el FN. Veamos cómo hacerlo en la siguiente sección.

Trabajar con múltiples trabajadores web

Actualmente, estamos manejando cada solicitud con un solo trabajador. Así, una solicitud entrante reemplazará a una anterior que aún está por terminar. Lo que queremos ahora es hacer un pequeño cambio para generar un nuevo trabajador web para cada solicitud. Mataremos a este trabajador una vez que haya terminado.

Abra src/index.js y mueva la línea que crea el trabajador web dentro del controlador de eventos de clic del botón. Ahora el controlador de eventos debería verse como a continuación.

 btn.addEventListener("click", (e) => { errPar.textContent = ""; const num = window.Number(input.value); if (num < 2) { errPar.textContent = "Please enter a number greater than 2"; return; } const worker = new window.Worker("src/fib-worker.js"); // this line has moved inside the event handler worker.postMessage({ num }); worker.onerror = (err) => err; worker.onmessage = (e) => { const { time, fibNum } = e.data; const resultDiv = document.createElement("div"); resultDiv.innerHTML = textCont(num, fibNum, time); resultDiv.className = "result-div"; resultsContainer.appendChild(resultDiv); worker.terminate() // this line terminates the worker }; });

Hicimos dos cambios.

  1. Movimos esta línea const worker = new window.Worker("src/fib-worker.js") dentro del controlador de eventos de clic del botón.
  2. Agregamos esta línea worker.terminate() para descartar el trabajador una vez que hayamos terminado con él.

Entonces, por cada clic del botón, creamos un nuevo trabajador para manejar el cálculo. Por lo tanto, podemos seguir cambiando la entrada, y cada resultado aparecerá en la pantalla una vez que finalice el cálculo. En la captura de pantalla a continuación, puede ver que los valores de 20 y 30 aparecen antes que los de 45. Pero comencé con 45 primero. Una vez que la función regresa para 20 y 30, se publicaron sus resultados y se despidió al trabajador. Cuando todo termine, no deberíamos tener ningún trabajador en la pestaña de fuentes.

mostrando números de Fibonacci con trabajadores despedidos
Ilustración de varios trabajadores independientes. (Vista previa grande)

Podríamos terminar este artículo aquí mismo, pero si se tratara de una aplicación de reacción, ¿cómo incorporaríamos a los trabajadores web? Ese es el enfoque de la siguiente sección.

Trabajadores web en React

Para comenzar, cree una nueva aplicación de reacción con CRA. Copie el archivo fib-worker.js en la carpeta public/ de su aplicación de reacción. Poner el archivo aquí se debe al hecho de que las aplicaciones React son aplicaciones de una sola página. Eso es lo único específico del uso del trabajador en una aplicación de reacción. Todo lo que sigue de aquí es puro React.

En la carpeta src/ cree un archivo helpers.js y exporte la función ordinal_suffix() desde él.

 // src/helpers.js export const ordinal_suffix = (num) => { // 1st, 2nd, 3rd, 4th, etc. const j = num % 10; const k = num % 100; switch (true) { case j === 1 && k !== 11: return num + "st"; case j === 2 && k !== 12: return num + "nd"; case j === 3 && k !== 13: return num + "rd"; default: return num + "th"; } };

Nuestra aplicación requerirá que mantengamos algún estado, así que cree otro archivo, src/reducer.js y péguelo en el reductor de estado.

 // src/reducers.js export const reducer = (state = {}, action) => { switch (action.type) { case "SET_ERROR": return { ...state, err: action.err }; case "SET_NUMBER": return { ...state, num: action.num }; case "SET_FIBO": return { ...state, computedFibs: [ ...state.computedFibs, { id: action.id, nth: action.nth, loading: action.loading }, ], }; case "UPDATE_FIBO": { const curr = state.computedFibs.filter((c) => c.id === action.id)[0]; const idx = state.computedFibs.indexOf(curr); curr.loading = false; curr.time = action.time; curr.fibNum = action.fibNum; state.computedFibs[idx] = curr; return { ...state }; } default: return state; } };

Repasemos cada tipo de acción uno tras otro.

  1. SET_ERROR : establece un estado de error cuando se activa.
  2. SET_NUMBER : establece el valor en nuestro cuadro de entrada en estado.
  3. SET_FIBO : agrega una nueva entrada a la matriz de FN calculados.
  4. UPDATE_FIBO : aquí buscamos una entrada en particular y la reemplazamos con un nuevo objeto que tiene el FN calculado y el tiempo necesario para calcularlo.

Usaremos este reductor en breve. Antes de eso, creemos el componente que mostrará los FN calculados. Cree un nuevo archivo src/Results.js y pegue el siguiente código.

 // src/Results.js import React from "react"; export const Results = (props) => { const { results } = props; return ( <div className="results-container"> {results.map((fb) => { const { id, nth, time, fibNum, loading } = fb; return ( <div key={id} className="result-div"> {loading ? ( <p> Calculating the{" "} <span className="bold"> {nth} </span>{" "} Fibonacci number... </p> ) : ( <> <p> Time: <span className="bold">{time} ms</span> </p> <p> <span className="bold"> {nth} </span>{" "} fibonnaci number:{" "} <span className="bold"> {fibNum} </span> </p> </> )} </div> ); })} </div> ); };

Con este cambio, comenzamos el proceso de convertir nuestro archivo index.html anterior a jsx. Este archivo tiene una responsabilidad: tomar una matriz de objetos que representan FN calculados y mostrarlos. La única diferencia con respecto a lo que teníamos antes es la introducción de un estado de carga . Entonces, ahora, cuando se ejecuta el cálculo, mostramos el estado de carga para que el usuario sepa que algo está sucediendo.

Pongamos las piezas finales actualizando el código dentro de src/App.js . El código es bastante largo, así que lo haremos en dos pasos. Agreguemos el primer bloque de código.

 import React from "react"; import "./App.css"; import { ordinal_suffix } from "./helpers"; import { reducer } from './reducer' import { Results } from "./Results"; function App() { const [info, dispatch] = React.useReducer(reducer, { err: "", num: "", computedFibs: [], }); const runWorker = (num, id) => { dispatch({ type: "SET_ERROR", err: "" }); const worker = new window.Worker('./fib-worker.js') worker.postMessage({ num }); worker.onerror = (err) => err; worker.onmessage = (e) => { const { time, fibNum } = e.data; dispatch({ type: "UPDATE_FIBO", id, time, fibNum, }); worker.terminate(); }; }; return ( <div> <div className="heading-container"> <h1>Computing the nth Fibonnaci number</h1> </div> <div className="body-container"> <p className="error"> {info.err} </p> // ... next block of code goes here ... // <Results results={info.computedFibs} /> </div> </div> ); } export default App;

Como de costumbre, traemos nuestras importaciones. Luego instanciamos una función de estado y actualización con el gancho useReducer. Luego definimos una función, runWorker() , que toma un número y una ID y se pone a llamar a un trabajador web para calcular el FN para ese número.

Tenga en cuenta que para crear el trabajador, pasamos una ruta relativa al constructor del trabajador. En tiempo de ejecución, nuestro código React se adjunta al archivo public/index.html , por lo que puede encontrar el archivo fib-worker.js en el mismo directorio. Cuando se completa el cálculo (activado por worker.onmessage ), se envía la acción UPDATE_FIBO y el trabajador finaliza después. Lo que tenemos ahora no es muy diferente de lo que teníamos antes.

En el bloque de retorno de este componente, representamos el mismo HTML que teníamos antes. También pasamos la matriz de números calculados al componente <Results /> para su representación.

Agreguemos el bloque final de código dentro de la declaración de return .

 <div className="input-div"> <input type="number" value={info.num} className="number-input" placeholder="Enter a number" onChange={(e) => dispatch({ type: "SET_NUMBER", num: window.Number(e.target.value), }) } /> <button className="btn-submit" onClick={() => { if (info.num < 2) { dispatch({ type: "SET_ERROR", err: "Please enter a number greater than 2", }); return; } const id = info.computedFibs.length; dispatch({ type: "SET_FIBO", id, loading: true, nth: ordinal_suffix(info.num), }); runWorker(info.num, id); }} > Calculate </button> </div>

Configuramos un controlador onChange en la entrada para actualizar la variable de estado info.num . En el botón, definimos un controlador de eventos onClick . Cuando se hace clic en el botón, verificamos si el número es mayor que 2. Tenga en cuenta que antes de llamar a runWorker() , primero enviamos una acción para agregar una entrada a la matriz de FN calculados. Es esta entrada la que se actualizará una vez que el trabajador termine su trabajo. De esta forma, cada entrada mantiene su posición en la lista, a diferencia de lo que teníamos antes.

Finalmente, copie el contenido de styles.css de antes y reemplace el contenido de App.css .

Ahora tenemos todo en su lugar. Ahora inicie su servidor de reacción y juegue con algunos números. Tome nota del estado de carga, que es una mejora de UX. Además, tenga en cuenta que la interfaz de usuario responde incluso cuando ingresa un número tan alto como 1000 y hace clic en "Calcular".

mostrando el estado de carga mientras el trabajador está activo.
Mostrando el estado de carga y el trabajador web activo. (Vista previa grande)

Tenga en cuenta el estado de carga y el trabajador activo. Una vez que se calcula el valor 46, el trabajador muere y el estado de carga se reemplaza por el resultado final.

  • El código fuente de esta aplicación React está disponible en Github y hay una aplicación alojada en vercel.

Conclusión

¡Uf! Ha sido un viaje largo, así que terminemos. Le animo a que eche un vistazo a la entrada de MDN para trabajadores web (vea la lista de recursos a continuación) para aprender otras formas de usar trabajadores web.

En este artículo, aprendimos qué son los trabajadores web y el tipo de problemas que deben resolver. También vimos cómo implementarlos usando JavaScript simple. Finalmente, vimos cómo implementar trabajadores web en una aplicación React.

Lo animo a que aproveche esta excelente API para brindar una mejor experiencia a sus usuarios.

Más recursos

  • Console.time() , documentos web de MDN
  • {JSON} Marcador de posición, sitio web oficial
  • Uso de Web Workers, documentos web de MDN
  • Número de Fibonacci, Wikipedia
  • Operador condicional (ternario), documentos web de MDN
  • Document , API web, documentos web de MDN
  • Primeros pasos, Crear aplicación React (docs)
  • Function.prototype.toString() , documentos web de MDN
  • IIFE, documentos web de MDN
  • workerSetup.js , Impresionantes tutoriales completos, GitHub
  • “Programación paralela en JavaScript usando trabajadores web”, Uday Hiwarale, Medium