Gestion des tâches de longue durée dans une application React avec des Web Workers

Publié: 2022-03-10
Résumé rapide ↬ Dans ce didacticiel, nous allons apprendre à utiliser l'API Web Worker pour gérer les tâches chronophages et bloquant l'interface utilisateur dans une application JavaScript en créant un exemple d'application Web qui exploite les Web Workers. Enfin, nous terminerons l'article en transférant le tout dans une application React.

Le temps de réponse est un gros problème quand il s'agit d'applications Web. Les utilisateurs exigent des réponses instantanées, peu importe ce que fait votre application. Qu'il s'agisse d'afficher uniquement le nom d'une personne ou de calculer des chiffres, les utilisateurs d'applications Web exigent que votre application réponde à leur commande à chaque fois. Parfois, cela peut être difficile à réaliser étant donné la nature monothread de JavaScript. Mais dans cet article, nous allons apprendre comment tirer parti de l'API Web Worker pour offrir une meilleure expérience.

En écrivant cet article, j'ai fait les hypothèses suivantes :

  1. Pour pouvoir suivre, vous devez avoir au moins une certaine familiarité avec JavaScript et l'API de document ;
  2. Vous devez également avoir une connaissance pratique de React afin de pouvoir démarrer avec succès un nouveau projet React à l'aide de Create React App.

Si vous avez besoin de plus d'informations sur ce sujet, j'ai inclus un certain nombre de liens dans la section "Autres ressources" pour vous aider à vous mettre au courant.

Commençons tout d'abord avec les Web Workers.

Qu'est-ce qu'un Web Worker ?

Pour comprendre les Web Workers et le problème qu'ils sont censés résoudre, il est nécessaire de comprendre comment le code JavaScript est exécuté au moment de l'exécution. Pendant l'exécution, le code JavaScript est exécuté de manière séquentielle et au tour par tour. Une fois qu'un morceau de code se termine, le suivant commence à s'exécuter, et ainsi de suite. En termes techniques, nous disons que JavaScript est monothread. Ce comportement implique qu'une fois qu'un morceau de code commence à s'exécuter, chaque code qui suit doit attendre que ce code termine son exécution. Ainsi, chaque ligne de code « bloque » l'exécution de tout ce qui vient après. Il est donc souhaitable que chaque morceau de code se termine le plus rapidement possible. Si un morceau de code prend trop de temps à se terminer, notre programme semblerait avoir cessé de fonctionner. Sur le navigateur, cela se manifeste par une page gelée qui ne répond pas. Dans certains cas extrêmes, l'onglet se fige complètement.

Imaginez rouler sur une voie unique. Si l'un des conducteurs devant vous s'arrête pour une raison quelconque, vous avez un embouteillage. Avec un programme comme Java, le trafic pourrait continuer sur d'autres voies. Ainsi, Java est dit multi-thread. Les Web Workers tentent d'apporter un comportement multithread à JavaScript.

La capture d'écran ci-dessous montre que l'API Web Worker est prise en charge par de nombreux navigateurs, vous devriez donc vous sentir en confiance pour l'utiliser.

Affichage du tableau de prise en charge des navigateurs pour les travailleurs Web
Prise en charge du navigateur Web Workers. ( Grand aperçu )

Les Web Workers s'exécutent dans des threads d'arrière-plan sans interférer avec l'interface utilisateur et ils communiquent avec le code qui les a créés au moyen de gestionnaires d'événements.

Une excellente définition d'un Web Worker vient de MDN :

« Un travailleur est un objet créé à l'aide d'un constructeur (par exemple Worker() qui exécute un fichier JavaScript nommé — ce fichier contient le code qui s'exécutera dans le thread de travail ; les travailleurs s'exécutent dans un autre contexte global différent de la window actuelle. Ainsi , en utilisant le raccourci de la window pour obtenir la portée globale actuelle (au lieu de self dans un Worker renverra une erreur.”

Un travailleur est créé à l'aide du constructeur Worker .

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

Il est possible d'exécuter la plupart du code à l'intérieur d'un web worker, à quelques exceptions près. Par exemple, vous ne pouvez pas manipuler le DOM depuis l'intérieur d'un worker. Il n'y a pas d'accès à l'API de document .

Les workers et le thread qui les génère s'envoient des messages à l'aide de la méthode postMessage() . De même, ils répondent aux messages à l'aide du gestionnaire d'événements onmessage . Il est important d'obtenir cette différence. L'envoi de messages est réalisé à l'aide d'une méthode ; la réception d'un message nécessite un gestionnaire d'événements. Le message reçu est contenu dans l'attribut de data de l'événement. Nous en verrons un exemple dans la section suivante. Mais permettez-moi de mentionner rapidement que le type de travailleur dont nous avons parlé s'appelle un « travailleur dévoué ». Cela signifie que le worker n'est accessible qu'au script qui l'a appelé. Il est également possible d'avoir un worker accessible depuis plusieurs scripts. Ceux-ci sont appelés travailleurs partagés et sont créés à l'aide du constructeur SharedWorker , comme indiqué ci-dessous.

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

Pour en savoir plus sur les Workers, veuillez consulter cet article MDN. Le but de cet article est de vous initier à l'utilisation des Web Workers. Allons-y en calculant le nième nombre de Fibonacci.

Plus après saut! Continuez à lire ci-dessous ↓

Calcul du nième nombre de Fibonacci

Remarque : pour cette section et les deux sections suivantes, j'utilise Live Server sur VSCode pour exécuter l'application. Vous pouvez certainement utiliser autre chose.

C'est la section que vous attendiez. Nous allons enfin écrire du code pour voir les Web Workers en action. Eh bien, pas si vite. Nous n'apprécierions pas le travail d'un Web Worker à moins de rencontrer le genre de problèmes qu'il résout. Dans cette section, nous allons voir un exemple de problème, et dans la section suivante, nous verrons comment un web worker nous aide à faire mieux.

Imaginez que vous construisiez une application Web permettant aux utilisateurs de calculer le nième nombre de Fibonacci. Si vous débutez avec le terme "nombre de Fibonacci", vous pouvez en savoir plus ici, mais en résumé, les nombres de Fibonacci sont une séquence de nombres tels que chaque nombre est la somme des deux nombres précédents.

Mathématiquement, il s'exprime par :

Ainsi les premiers chiffres de la suite sont :

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

Dans certaines sources, la séquence commence à F 0 = 0 , auquel cas la formule ci-dessous est valable pour n > 1 :

Dans cet article, nous commencerons à F 1 = 1. Une chose que nous pouvons voir tout de suite à partir de la formule est que les nombres suivent un modèle récursif. La tâche à accomplir consiste maintenant à écrire une fonction récursive pour calculer le nième nombre de Fibonacci (FN).

Après quelques essais, je pense que vous pouvez facilement trouver la fonction ci-dessous.

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

La fonction est simple. Si n est inférieur à 2, renvoie n (ou 1), sinon, renvoie la somme des n-1 et n-2 FN. Avec les fonctions fléchées et l'opérateur ternaire, nous pouvons proposer un one-liner.

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

Cette fonction a une complexité temporelle de 0(2 n ) . Cela signifie simplement que lorsque la valeur de n augmente, le temps nécessaire pour calculer la somme augmente de façon exponentielle. Cela crée une tâche très longue qui pourrait potentiellement interférer avec notre interface utilisateur, pour de grandes valeurs de n. Voyons cela en action.

Remarque : Ce n'est en aucun cas la meilleure façon de résoudre ce problème particulier. Mon choix d'utiliser cette méthode est dans le but de cet article.

Pour commencer, créez un nouveau dossier et nommez-le comme vous le souhaitez. Maintenant, dans ce dossier, créez un dossier src/ . Créez également un fichier index.html dans le dossier racine. Dans le dossier src/ , créez un fichier nommé index.js .

Ouvrez index.html et ajoutez le code HTML suivant.

 <!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>

Cette partie est très simple. Tout d'abord, nous avons un titre. Ensuite, nous avons un conteneur avec une entrée et un bouton. Un utilisateur entrerait un nombre puis cliquerait sur "Calculer". Nous avons également un conteneur pour contenir le résultat du calcul. Enfin, nous incluons le fichier src/index.js dans une balise de script .

Vous pouvez supprimer le lien de la feuille de style. Mais si vous manquez de temps, j'ai défini quelques CSS que vous pouvez utiliser. Créez simplement le fichier styles.css dans le dossier racine et ajoutez les styles ci-dessous :

 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; }

Maintenant, ouvrez src/index.js développons-le lentement. Ajoutez le code ci-dessous.

 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> `; };

Ici, nous avons trois fonctions. La première est la fonction que nous avons vue précédemment pour calculer le nième FN. La deuxième fonction est juste une fonction utilitaire pour attacher un suffixe approprié à un nombre entier. La troisième fonction prend des arguments et génère un balisage que nous insérerons plus tard dans le DOM. Le premier argument est le nombre dont FN est calculé. Le deuxième argument est le FN calculé. Le dernier argument est le temps nécessaire pour effectuer le calcul.

Toujours dans src/index.js , ajoutez le code ci-dessous juste en dessous du précédent.

 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); });

Tout d'abord, nous utilisons l'API de document pour obtenir les nœuds DOM dans notre fichier HTML. Nous obtenons une référence au paragraphe où nous afficherons les messages d'erreur ; l'entrée; le bouton de calcul et le conteneur où nous afficherons nos résultats.

Ensuite, nous attachons un gestionnaire d'événement "click" au bouton. Lorsque le bouton est cliqué, nous prenons tout ce qui se trouve à l'intérieur de l'élément d'entrée et le convertissons en un nombre, si nous obtenons quelque chose de moins que 2, nous affichons un message d'erreur et revenons. Si nous obtenons un nombre supérieur à 2, nous continuons. Tout d'abord, nous enregistrons l'heure actuelle. Après cela, nous calculons le FN. Lorsque cela se termine, nous obtenons une différence de temps qui représente la durée du calcul. Dans la partie restante du code, nous créons un nouveau div . Nous définissons ensuite son code HTML interne comme étant la sortie de la fonction textCont() que nous avons définie précédemment. Enfin, nous lui ajoutons une classe (pour le style) et l'ajoutons au conteneur de résultats. L'effet de ceci est que chaque calcul apparaîtra dans une div séparée en dessous de la précédente.

Affichage des nombres de Fibonacci calculés jusqu'à 43
Quelques nombres de Fibonacci. ( Grand aperçu )

Nous pouvons voir que lorsque le nombre augmente, le temps de calcul augmente également (exponentiellement). Par exemple, de 30 à 35, nous avons fait passer le temps de calcul de 13 ms à 130 ms. On peut encore considérer ces opérations comme « rapides ». A 40 nous voyons un temps de calcul supérieur à 1 seconde. Sur ma machine, c'est là que je commence à remarquer que la page ne répond plus. À ce stade, je ne peux plus interagir avec la page pendant que le calcul est en cours. Je ne peux pas me concentrer sur l'entrée ou faire autre chose.

Rappelez-vous quand nous avons parlé de JavaScript en tant que thread unique ? Eh bien, ce thread a été "bloqué" par ce calcul de longue durée, donc tout le reste doit "attendre" qu'il se termine. Il peut commencer à une valeur inférieure ou supérieure sur votre machine, mais vous êtes obligé d'atteindre ce point. Notez qu'il a fallu près de 10 secondes pour calculer celui de 44. S'il y avait d'autres choses à faire sur votre application Web, eh bien, l'utilisateur doit attendre que Fib(44) se termine avant de pouvoir continuer. Mais si vous avez déployé un Web Worker pour gérer ce calcul, vos utilisateurs pourraient continuer avec autre chose pendant que cela s'exécute.

Voyons maintenant comment les web workers nous aident à surmonter ce problème.

Un exemple de Web Worker en action

Dans cette section, nous allons déléguer le travail de calcul du nième FN à un web worker. Cela aidera à libérer le thread principal et à garder notre interface utilisateur réactive pendant que le calcul est en cours.

Démarrer avec les web workers est étonnamment simple. Voyons comment. Créez un nouveau fichier src/fib-worker.js . et entrez le code suivant.

 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, }); };

Notez que nous avons déplacé la fonction qui calcule le nième nombre de Fibonacci, fib à l'intérieur de ce fichier. Ce fichier sera exécuté par notre web worker.

Rappelons dans la section Qu'est-ce qu'un web worker , nous avons mentionné que les web workers et leur parent communiquent à l'aide du gestionnaire d'événements onmessage et de la méthode postMessage() . Ici, nous utilisons le gestionnaire d'événements onmessage pour écouter les messages du script parent. Une fois que nous recevons un message, nous déstructurons le numéro de l'attribut de données de l'événement. Ensuite, nous obtenons l'heure actuelle et commençons le calcul. Une fois le résultat prêt, nous utilisons la méthode postMessage() pour publier les résultats dans le script parent.

Ouvrez src/index.js quelques modifications.

 ... 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); }; });

La première chose à faire est de créer le web worker à l'aide du constructeur Worker . Ensuite, dans l'écouteur d'événement de notre bouton, nous envoyons un numéro au travailleur en utilisant worker.postMessage({ num }) . Après cela, nous avons défini une fonction pour écouter les erreurs dans le travailleur. Ici, nous renvoyons simplement l'erreur. Vous pouvez certainement faire plus si vous le souhaitez, comme l'afficher dans DOM. Ensuite, nous écoutons les messages du travailleur. Une fois que nous recevons un message, nous déstructurons time et fibNum , et continuons le processus de les afficher dans le DOM.

Notez qu'à l'intérieur du web worker, l'événement onmessage est disponible dans la portée du worker, donc nous aurions pu l'écrire comme self.onmessage et self.postMessage() . Mais dans le script parent, nous devons les attacher au travailleur lui-même.

Dans la capture d'écran ci-dessous, vous verrez le fichier Web Worker dans l'onglet Sources de Chrome Dev Tools. Ce que vous devriez remarquer, c'est que l'interface utilisateur reste réactive quel que soit le numéro que vous entrez. Ce comportement est la magie des web workers.

Affichage d'un fichier de travailleur Web actif
Un fichier de travail Web en cours d'exécution. ( Grand aperçu )

Nous avons fait beaucoup de progrès avec notre application Web. Mais il y a autre chose que nous pouvons faire pour l'améliorer. Notre implémentation actuelle utilise un seul travailleur pour gérer chaque calcul. Si un nouveau message arrive alors qu'un autre est en cours d'exécution, l'ancien est remplacé. Pour contourner ce problème, nous pouvons créer un nouveau travailleur pour chaque appel afin de calculer le FN. Voyons comment procéder dans la section suivante.

Travailler avec plusieurs travailleurs Web

Actuellement, nous traitons chaque demande avec un seul travailleur. Ainsi, une demande entrante remplacera une précédente qui n'est pas encore terminée. Ce que nous voulons maintenant, c'est apporter un petit changement pour générer un nouveau Web Worker pour chaque requête. Nous tuerons cet ouvrier une fois que ce sera fait.

Ouvrez src/index.js et déplacez la ligne qui crée le web worker dans le gestionnaire d'événements click du bouton. Maintenant, le gestionnaire d'événements devrait ressembler à ci-dessous.

 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 }; });

Nous avons apporté deux modifications.

  1. Nous avons déplacé cette ligne const worker = new window.Worker("src/fib-worker.js") à l'intérieur du gestionnaire d'événements click du bouton.
  2. Nous avons ajouté cette ligne worker.terminate() pour supprimer le travailleur une fois que nous en avons terminé.

Ainsi, pour chaque clic sur le bouton, nous créons un nouveau travailleur pour gérer le calcul. Ainsi, nous pouvons continuer à modifier l'entrée et chaque résultat apparaîtra à l'écran une fois le calcul terminé. Dans la capture d'écran ci-dessous, vous pouvez voir que les valeurs de 20 et 30 apparaissent avant celle de 45. Mais j'ai commencé 45 en premier. Une fois que la fonction est retournée pour 20 et 30, leurs résultats ont été publiés et le travailleur s'est terminé. Lorsque tout est terminé, nous ne devrions plus avoir de nœuds de calcul dans l'onglet sources.

montrant les nombres de Fibonacci avec les travailleurs licenciés
Illustration de plusieurs travailleurs indépendants. ( Grand aperçu )

Nous pourrions terminer cet article ici, mais s'il s'agissait d'une application de réaction, comment pourrions-nous y intégrer les travailleurs du Web. C'est l'objet de la section suivante.

Travailleurs du Web en réaction

Pour commencer, créez une nouvelle application de réaction à l'aide de CRA. Copiez le fichier fib-worker.js dans le dossier public/ de votre application React. Mettre le fichier ici découle du fait que les applications React sont des applications d'une seule page. C'est à peu près la seule chose spécifique à l'utilisation du travailleur dans une application de réaction. Tout ce qui en découle est du pur React.

Dans le dossier src/ , créez un fichier helpers.js et exportez-en la fonction ordinal_suffix() .

 // 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"; } };

Notre application nous obligera à maintenir un état, alors créez un autre fichier, src/reducer.js et collez-le dans le réducteur d'état.

 // 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; } };

Passons en revue chaque type d'action l'un après l'autre.

  1. SET_ERROR : définit un état d'erreur lorsqu'il est déclenché.
  2. SET_NUMBER : définit la valeur dans notre zone de saisie sur state.
  3. SET_FIBO : ajoute une nouvelle entrée au tableau des FN calculés.
  4. UPDATE_FIBO : ici, nous recherchons une entrée particulière et la remplaçons par un nouvel objet qui a le FN calculé et le temps nécessaire pour le calculer.

Nous utiliserons ce réducteur sous peu. Avant cela, créons le composant qui affichera les FN calculés. Créez un nouveau fichier src/Results.js et collez-y le code ci-dessous.

 // 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> ); };

Avec ce changement, nous commençons le processus de conversion de notre précédent fichier index.html en jsx. Ce fichier a une responsabilité : prendre un tableau d'objets représentant des FN calculés et les afficher. La seule différence par rapport à ce que nous avions auparavant est l'introduction d'un état de chargement . Alors maintenant, lorsque le calcul est en cours, nous affichons l'état de chargement pour informer l'utilisateur que quelque chose se passe.

Mettons les dernières pièces en mettant à jour le code dans src/App.js . Le code est assez long, nous allons donc le faire en deux étapes. Ajoutons le premier bloc de code.

 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;

Comme d'habitude, nous apportons nos importations. Ensuite, nous instancions une fonction d'état et de mise à jour avec le crochet useReducer. Nous définissons ensuite une fonction, runWorker() , qui prend un nombre et un ID et définit l'appel d'un web worker pour calculer le FN pour ce nombre.

Notez que pour créer le travailleur, nous passons un chemin relatif au constructeur du travailleur. Lors de l'exécution, notre code React est attaché au fichier public/index.html , il peut donc trouver le fichier fib-worker.js dans le même répertoire. Lorsque le calcul est terminé (déclenché par worker.onmessage ), l'action UPDATE_FIBO est envoyée et le travailleur s'est terminé par la suite. Ce que nous avons maintenant n'est pas très différent de ce que nous avions auparavant.

Dans le bloc de retour de ce composant, nous rendons le même code HTML que nous avions auparavant. Nous transmettons également le tableau des nombres calculés au composant <Results /> pour le rendu.

Ajoutons le dernier bloc de code à l'intérieur de l'instruction 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>

Nous définissons un gestionnaire onChange sur l'entrée pour mettre à jour la variable d'état info.num . Sur le bouton, nous définissons un gestionnaire d'événement onClick . Lorsque le bouton est cliqué, nous vérifions si le nombre est supérieur à 2. Notez qu'avant d'appeler runWorker() , nous envoyons d'abord une action pour ajouter une entrée au tableau des FN calculés. C'est cette entrée qui sera mise à jour une fois que le travailleur aura terminé son travail. De cette façon, chaque entrée conserve sa position dans la liste, contrairement à ce que nous avions auparavant.

Enfin, copiez le contenu de styles.css d'avant et remplacez le contenu de App.css .

Nous avons maintenant tout en place. Maintenant, démarrez votre serveur de réaction et jouez avec quelques chiffres. Prenez note de l'état de chargement, qui est une amélioration UX. Notez également que l'interface utilisateur reste réactive même lorsque vous entrez un nombre aussi élevé que 1000 et cliquez sur "Calculer".

montrant l'état de chargement pendant que le travailleur est actif.
Affichage de l'état de chargement et du travailleur Web actif. ( Grand aperçu )

Notez l'état de chargement et le travailleur actif. Une fois la 46e valeur calculée, le travailleur est tué et l'état de chargement est remplacé par le résultat final.

  • Le code source de cette application React est disponible sur Github et il existe une application hébergée sur vercel.

Conclusion

Phew! Le trajet a été long, alors terminons-le. Je vous encourage à consulter l'entrée MDN pour les web workers (voir la liste des ressources ci-dessous) pour découvrir d'autres façons d'utiliser les web workers.

Dans cet article, nous avons découvert ce que sont les web workers et le type de problèmes qu'ils sont censés résoudre. Nous avons également vu comment les implémenter en utilisant du JavaScript simple. Enfin, nous avons vu comment implémenter des web workers dans une application React.

Je vous encourage à profiter de cette excellente API pour offrir une meilleure expérience à vos utilisateurs.

Autres ressources

  • Console.time() , documentation Web MDN
  • {JSON} Espace réservé, site officiel
  • Utilisation des Web Workers, documentation Web MDN
  • Nombre de Fibonacci, Wikipédia
  • Opérateur conditionnel (ternaire), documentation Web MDN
  • Document , API Web, documentation Web MDN
  • Mise en route, Créer une application React (docs)
  • Function.prototype.toString() , documentation Web MDN
  • Documents Web IIFE, MDN
  • workerSetup.js , Tutoriels Fullstack géniaux, GitHub
  • "Programmation parallèle en JavaScript à l'aide de Web Workers", Uday Hiwarale, Medium