قوائم المهام في TypeScript: دليل عملي

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

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

ابدأ الدورة

العمل مع قوائم المهام في TypeScript: عمليًا

\n

في تطبيقات الويب الحديثة، غالبًا ما تكون هناك حاجة لتنفيذ عمليات طويلة أو كثيفة الاستخدام للموارد بشكل غير متزامن، دون حظر التدفق الرئيسي للمستخدم. إرسال البريد الإلكتروني الجماعي، إنشاء تقارير PDF، معالجة الصور المرفوعة - كلها مرشحة مثالية للنقل إلى مهام خلفية. قائمة المهام (task queue) هي نمط معماري مثبت يسمح بتنظيم تنفيذ موثوق لمثل هذه العمليات. في هذه المقالة، سنشرح كيفية إنشاء واستخدام قوائم المهام في TypeScript عمليًا، باستخدام المكتبات والأساليب الشائعة.

\n

سننظر في إنشاء قائمة مهام بسيطة، والعمل مع الأولويات، ومعالجة الأخطاء، بالإضافة إلى التكامل مع Redis عبر مكتبة BullMQ. جميع الأمثلة ستكون مكتوبة بلغة TypeScript مع التركيز على الكتابة الصارمة للأنواع.

\n\n

1. الأساسيات: إنشاء قائمة مهام بسيطة

\n

لنبدأ بتنفيذ قائمة مهام بسيطة بدون تبعيات خارجية. سيساعدنا هذا على فهم المبادئ الأساسية للعمل. ستخزن قائمتنا وظائف المهام وتنفذها بالتسلسل.

\n
type Task<T> = () => Promise<T>;\n\ninterface TaskItem<T> {\n  task: Task<T>;\n  resolve: (value: T | PromiseLike<T>) => void;\n  reject: (reason?: any) => void;\n}\n\nclass SimpleQueue {\n  private queue: TaskItem<any>[] = [];\n  private isProcessing = false;\n\n  add<T>(task: Task<T>): Promise<T> {\n    return new Promise<T>((resolve, reject) => {\n      this.queue.push({ task, resolve, reject });\n      this.process();\n    });\n  }\n\n  private async process(): Promise<void> {\n    if (this.isProcessing || this.queue.length === 0) return;\n    this.isProcessing = true;\n\n    const item = this.queue.shift()!;\n    try {\n      const result = await item.task();\n      item.resolve(result);\n    } catch (error) {\n      item.reject(error);\n    } finally {\n      this.isProcessing = false;\n      this.process();\n    }\n  }\n}\n\n// الاستخدام\nconst queue = new SimpleQueue();\n\nqueue.add(async () => {\n  console.log('بدأت المهمة 1');\n  await new Promise(r => setTimeout(r, 1000));\n  console.log('اكتملت المهمة 1');\n  return 'النتيجة 1';\n});\n\nqueue.add(async () => {\n  console.log('بدأت المهمة 2');\n  return 'النتيجة 2';\n});
\n

يوضح هذا المثال الآلية الأساسية: تتم إضافة المهام إلى مصفوفة وتنفيذها بالتسلسل. كل مهمة تُرجع Promise، مما يسمح بالعمل بسهولة مع النتائج. ومع ذلك، فإن هذا النهج له قيود: لا يوجد دعم للأولويات، أو إعادة المحاولة، أو حفظ الحالة عند إعادة تشغيل التطبيق.

\n\n

2. تحديد الأولويات ومعالجة الأخطاء

\n

في المشاريع الحقيقية، غالبًا ما يكون للمهام أهمية مختلفة. على سبيل المثال، يجب تنفيذ مهمة إرسال بريد إلكتروني معاملة قبل مهمة تنظيف الملفات المؤقتة. دعنا نضيف الأولويات إلى قائمتنا.

\n
type Priority = 'high' | 'medium' | 'low';\n\ninterface PriorityTask<T> {\n  task: Task<T>;\n  priority: Priority;\n  resolve: (value: T) => void;\n  reject: (reason?: any) => void;\n  retries: number;\n}\n\nclass PriorityQueue {\n  private high: PriorityTask<any>[] = [];\n  private medium: PriorityTask<any>[] = [];\n  private low: PriorityTask<any>[] = [];\n  private isProcessing = false;\n  private maxRetries = 3;\n\n  add<T>(task: Task<T>, priority: Priority = 'medium'): Promise<T> {\n    return new Promise<T>((resolve, reject) => {\n      const taskItem: PriorityTask<T> = {\n        task,\n        priority,\n        resolve,\n        reject,\n        retries: 0\n      };\n      this.getQueue(priority).push(taskItem);\n      this.process();\n    });\n  }\n\n  private getQueue(priority: Priority): PriorityTask<any>[] {\n    switch (priority) {\n      case 'high': return this.high;\n      case 'medium': return this.medium;\n                

المدونات

توصيات الكتب