العمل مع قوائم المهام في JavaScript عمليًا في Node.js

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا نحتاج إلى قوائم المهام في JavaScript؟

\n

في تطوير الويب الحديث باستخدام Node.js، غالبًا ما تظهر مهام لا يمكن تنفيذها فورًا ضمن طلب HTTP واحد. إرسال البريد الإلكتروني الجماعي، إنشاء تقارير PDF، معالجة الصور، المزامنة مع واجهات برمجة التطبيقات الخارجية - كل هذه العمليات تتطلب وقتًا وموارد. إذا تم تنفيذها بشكل متزامن، فسينتظر المستخدم الرد لفترة طويلة جدًا، وقد يصبح الخادم مثقلًا.

\n

قوائم المهام (Job Queues) تحل هذه المشكلة. فهي تسمح بتأجيل تنفيذ العمليات الثقيلة، توزيع الحمل، ضمان إعادة المعالجة عند حدوث أعطال، وتوسيع النظام أفقيًا. في هذه المقالة، سنشرح الجوانب العملية للعمل مع قوائم المهام في JavaScript (Node.js): من النظرية إلى كتابة كود عملي باستخدام المكتبات الشائعة.

\n\n

المفاهيم الأساسية لقوائم المهام

\n

قبل الانتقال إلى الكود، من المهم فهم العناصر الرئيسية لأي قائمة مهام:

\n
    \n
  • Job (مهمة) - وحدة العمل التي يجب تنفيذها. تحتوي عادةً على بيانات (payload) ومعلومات وصفية (أولوية، محاولات، حالة).
  • \n
  • Queue (قائمة انتظار) - هيكل بيانات (غالبًا يعتمد على Redis) يخزن المهام بترتيب وصولها.
  • \n
  • Worker (عامل) - عملية أو خيط يسحب المهام من قائمة الانتظار وينفذها.
  • \n
  • Producer (منتج) - جزء النظام الذي يضيف المهام إلى قائمة الانتظار.
  • \n
  • Consumer (مستهلك) - مرادف للعامل الذي يعالج المهام.
  • \n
\n

في Node.js، أشهر المكتبات للعمل مع قوائم المهام هي Bull و Kue. كلاهما يستخدم Redis كوسيط للرسائل. Bull أكثر حداثة وأداءً، بينما Kue أبسط للبدء. في هذه المقالة، سنركز على Bull كحل موصى به للإنتاج.

\n\n

تنفيذ عملي لقائمة انتظار باستخدام Bull

\n

التثبيت والإعداد

\n

للبدء، قم بتثبيت Redis (محليًا أو عبر Docker) وأضف المكتبة إلى المشروع:

\n
npm install bull
\n

التهيئة الأساسية لقائمة الانتظار تبدو كالتالي:

\n
const Queue = require('bull');\nconst imageQueue = new Queue('معالجة الصور', {\n  redis: { host: 'localhost', port: 6379 }\n});
\n\n

إضافة المهام (Producer)

\n

المنتج يضيف مهمة مع البيانات والخيارات:

\n
app.post('/upload', async (req, res) => {\n  const job = await imageQueue.add({\n    imageUrl: req.body.url,\n    userId: req.user.id\n  }, {\n    attempts: 3,        // عدد المحاولات عند الخطأ\n    backoff: 5000,      // التأخير بين المحاولات (مللي ثانية)\n    removeOnComplete: true // حذف المهمة بعد النجاح\n  });\n  res.json({ jobId: job.id });\n});
\n\n

معالجة المهام (Worker)

\n

العامل هو عملية منفصلة (أو نفس العملية ولكن غير متزامنة) تستمع إلى قائمة الانتظار:

\n
imageQueue.process(async (job, done) => {\n  try {\n    const { imageUrl, userId } = job.data;\n    console.log(`معالجة الصورة ${imageUrl} للمستخدم ${userId}`);\n    // هنا منطقك: تحميل، تغيير الحجم، حفظ\n    await processImage(imageUrl);\n    done(); // إشارة اكتمال ناجح\n  } catch (error) {\n    done(new Error('خطأ في المعالجة: ' + error.message));\n  }\n});
\n\n

معالجة الأخطاء وإعادة المحاولة

\n

يقوم Bull تلقائيًا بإعادة محاولة المهمة إذا انتهى العامل بخطأ. يمكنك تخصيص استراتيجية إعادة المحاولة:

\n
const job = await imageQueue.add(data, {\n  attempts: 5,\n  backoff: { type: 'exponential', delay: 2000 }\n});
\n

يمكنك أيضًا الاستماع إلى أحداث قائمة الانتظار للمراقبة:

\n
imageQueue.on('completed', (job, result) => {\n  console.log(`المهمة ${job.id} اكتملت`);\n});\nimageQueue.on('failed', (job, err) => {\n  console.error(`المهمة ${job.id} فشلت: ${err.mes                

المدونات

توصيات الكتب