Travailler avec les files d'attente de tâches en JavaScript (Node.js)
Dans les applications web modernes et les architectures de microservices, il est souvent nécessaire d'exécuter des tâches en arrière-plan qui ne doivent pas bloquer le thread d'exécution principal. Les files d'attente de tâches sont un outil puissant qui aide à gérer les opérations asynchrones, garantissant fiabilité, évolutivité et tolérance aux pannes. Dans cet article, nous examinerons en détail comment travailler avec les files d'attente de tâches dans l'environnement Node.js, passerons en revue les principales bibliothèques et écrirons des exemples de code pratiques.
Qu'est-ce qu'une file d'attente de tâches et pourquoi est-elle nécessaire ?
Une file d'attente de tâches est une structure de données fonctionnant sur le principe FIFO (First In, First Out). Les tâches sont placées dans la file et traitées par les workers dans l'ordre de leur arrivée. Les principaux cas d'utilisation incluent :
- Envoi de notifications par email — pas besoin de faire attendre l'utilisateur pendant l'envoi de l'email.
- Génération de rapports ou de PDF — des opérations gourmandes en ressources qu'il est préférable d'effectuer de manière asynchrone.
- Traitement d'images téléchargées — redimensionnement, compression, ajout de filigranes.
- Intégration avec des API externes — exécution de requêtes avec limitation de débit.
- Tâches planifiées (cron) — nettoyage régulier de la base de données ou mise à jour du cache.
L'utilisation de files d'attente permet de décharger le serveur principal, d'augmenter la tolérance aux pannes (si un worker échoue, la tâche sera transférée à un autre) et de garantir l'exécution des tâches même en cas de pannes temporaires.
File d'attente simple en mémoire dans Node.js
Pour commencer, implémentons une file d'attente simple sans dépendances externes. Cette approche convient au prototypage ou aux très petits projets, mais n'est pas recommandée pour la production en raison de l'absence de persistance.
class SimpleQueue { constructor() { this.queue = []; this.processing = false; }
enqueue(task) { this.queue.push(task); this.processNext(); }
async processNext() { if (this.processing || this.queue.length === 0) return; this.processing = true; const task = this.queue.shift(); try { await task(); } catch (err) { console.error('Task execution error:', err); } finally { this.processing = false; this.processNext(); } }}
// Usage exampleconst queue = new SimpleQueue();
queue.enqueue(async () => { console.log('Task 1: Sending email');});
queue.enqueue(async () => { console.log('Task 2: Generating report');});
console.log('Tasks added to queue');Ce code illustre les mécanismes de base : les tâches sont exécutées séquentiellement, l'une après l'autre. Cependant, lorsque le serveur redémarre, toutes les tâches non traitées seront perdues.
Bull — File d'attente professionnelle sur Redis
Pour les projets sérieux en Node.js, la bibliothèque Bull est la référence de facto. Elle utilise Redis comme backend, offrant persistance, support des priorités, tentatives et tâches différées.
Installation :
npm install bullExemple d'un producteur et d'un consommateur simples :
const Queue = require('bull');
// Create a queue named 'email-queue'const emailQueue = new Queue('email-queue', { redis: { host: 'localhost', port: 6379 }});
// Worker function: processes tasksemailQueue.process(async (job) => { const { to, subject, body } = job.data; console.log(`Sending email to ${to}: ${subject}`); // Here would be the actual email sending code await new Promise(resolve => setTimeout(resolve, 1000)); return { success: true };});
// Adding tasks