Concurrence et multithreading en JavaScript : Event Loop et Worker Threads

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course
```html

Introduction : Mythes et réalité du multithreading en JavaScript

JavaScript est traditionnellement associé à un modèle d'exécution monothread. De nombreux développeurs, surtout ceux venant du monde du C++ ou de Java, considèrent cela comme une limitation. Cependant, l'écosystème moderne de Node.js offre des outils puissants pour travailler avec la concurrence et, plus important encore, avec le véritable multithreading. Dans cet article, nous allons voir comment fonctionne l'Event Loop, quand utiliser Worker Threads et comment construire correctement des applications à forte charge.

Contrairement au multithreading classique, où chaque thread a sa propre pile et peut s'exécuter en parallèle sur différents cœurs de processeur, JavaScript dans le navigateur et Node.js utilise un modèle de concurrence basé sur les événements. Cependant, avec l'apparition du module worker_threads dans Node.js, nous avons obtenu la possibilité d'un véritable parallélisme.

Partie principale

1. Event Loop : Le cœur de la concurrence dans Node.js

L'Event Loop est un mécanisme qui permet à Node.js d'effectuer des opérations d'entrée/sortie (I/O) non bloquantes, malgré le monothreading de JavaScript. Il vérifie constamment la file d'attente des tâches et les exécute au fur et à mesure qu'elles arrivent.

Les phases principales de l'Event Loop :

  • Timers — exécution des callbacks planifiés via setTimeout() et setInterval().
  • I/O callbacks — traitement des opérations d'entrée/sortie terminées.
  • Idle, prepare — phases internes de Node.js.
  • Poll — réception de nouveaux événements I/O et exécution de leurs callbacks.
  • Check — exécution des callbacks de setImmediate().
  • Close callbacks — traitement des événements de fermeture (par exemple, socket.on('close', ...)).

Exemple de fonctionnement de l'Event Loop :

const fs = require('fs');

console.log('Début du programme');

setTimeout(() => {
    console.log('Timer déclenché');
}, 0);

setImmediate(() => {
    console.log('setImmediate exécuté');
});

fs.readFile(__filename, () => {
    console.log('Fichier lu');
});

console.log('Fin du programme');

// Affichage :
// Début du programme
// Fin du programme
// setImmediate exécuté
// Timer déclenché
// Fichier lu

Il est important de comprendre : l'Event Loop ne rend pas le code multithread, il répartit simplement efficacement le temps entre les tâches. Si une tâche occupe le CPU trop longtemps (par exemple, une boucle complexe), tout l'Event Loop est bloqué.

2. Le problème du blocage et la solution : Worker Threads

Pour les tâches intensives en CPU (chiffrement, traitement d'images, analyse de grandes données), l'Event Loop n'est pas adapté — il bloquera tout le serveur. C'est là que les Worker Threads viennent à la rescousse.

Worker Threads est un module Node.js qui permet de créer de véritables threads du système d'exploitation. Chaque worker possède sa propre instance de V8 et son propre Event Loop, ce qui permet d'exécuter le code en parallèle.

Exemple de base d'utilisation de Worker Threads :

// main.js
const { Worker } = require('worker_threads');

function runWorker(workerData) {
    return new Promise((resolve, reject) => {
        const worker = new Worker('./worker.js', { workerData });
        
        worker.on('message', resolve);
        worker.on('error', reject);
        worker.on('exit', (code) => {
            if (code !== 0) {
                reject(new Error(`Worker arrêté avec le code ${code}`));
            }
        });
    });
}

async function main() {
    console.log('Lancement du worker...');
    const result = await runWorker(42);
    console.log('Résultat :', result);
}

main().catch(console.error);

// worker.js
const { parentPort, workerData } = require('worker_threads');

// Tâche intensive en CPU
function fibonacci(n) {
    if (n < 2) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

const result = fibonacci(workerData);
parentPort.postMessage(result);
```

Blogs

Book Recommendations