مقدمة في قوائم المهام في TypeScript
قائمة المهام (Queue) هي واحدة من هياكل البيانات الأساسية في البرمجة، تعمل وفق مبدأ FIFO (First In, First Out - "من يدخل أولاً يخرج أولاً"). تخيل طابوراً عادياً في متجر: أول زبون يقف في الطابور يتم خدمته أولاً. في البرمجة، تُستخدم قائمة المهام في كل مكان: من معالجة الطلبات على الخادم إلى إدارة العمليات غير المتزامنة في المتصفح.
في هذه المقالة، سنشرح كيفية إنشاء واستخدام قائمة المهام في TypeScript. ستتعلم كيفية كتابة أنواع القائمة، وتنفيذ الطرق الأساسية، وتطبيقها في مشاريع حقيقية. المحتوى موجه للمطورين المبتدئين الذين لديهم معرفة أساسية بـ TypeScript.
العمليات الأساسية مع القائمة
يجب أن تدعم أي قائمة مهام عدة عمليات أساسية:
- enqueue (إضافة عنصر إلى نهاية القائمة);
- dequeue (استخراج عنصر من بداية القائمة);
- peek (عرض العنصر الأول دون إزالته);
- isEmpty (التحقق مما إذا كانت القائمة فارغة);
- size (معرفة عدد العناصر).
في TypeScript، يمكننا تنفيذ قائمة انتظار باستخدام مصفوفة أو قائمة مرتبطة. للتبسيط، سنبدأ بالمصفوفة.
تنفيذ قائمة انتظار باستخدام مصفوفة
لننشئ كلاس Queue باستخدام الأدوية (Generics) - وهذا سيسمح بتخزين بيانات من أي نوع:
class Queue<T> { private items: T[] = [];
// إضافة عنصر إلى نهاية القائمة enqueue(element: T): void { this.items.push(element); }
// إزالة وإرجاع العنصر الأول dequeue(): T | undefined { return this.items.shift(); }
// عرض العنصر الأول دون إزالته peek(): T | undefined { return this.items[0]; }
// التحقق مما إذا كانت القائمة فارغة isEmpty(): boolean { return this.items.length === 0; }
// الحصول على حجم القائمة size(): number { return this.items.length; }}الآن يمكننا إنشاء قائمة لأي نوع من البيانات:
// قائمة نصوصconst stringQueue = new Queue<string>();stringQueue.enqueue('المهمة 1');stringQueue.enqueue('المهمة 2');console.log(stringQueue.dequeue()); // 'المهمة 1'
// قائمة أرقامconst numberQueue = new Queue<number>();numberQueue.enqueue(10);numberQueue.enqueue(20);console.log(numberQueue.peek()); // 10عيوب التنفيذ باستخدام المصفوفة
طريقة shift() في JavaScript/TypeScript لها تعقيد O(n)، لأنه بعد إزالة العنصر الأول، يتم إزاحة جميع العناصر المتبقية. بالنسبة للبيانات الكبيرة، هذا غير فعال. الحل هو تنفيذ قائمة انتظار باستخدام كائن أو قائمة مرتبطة.
قائمة انتظار محسّنة باستخدام كائن
لتجنب إزاحة العناصر، سنقوم بتخزين العناصر في كائن وتتبع مؤشرات البداية والنهاية:
class OptimizedQueue<T> { private items: Record<number, T> = {}; private head: number = 0; private tail: number = 0;
enqueue(element: T): void { this.items[this.tail] = element; this.tail++; }
dequeue(): T | undefined { if (this.isEmpty()) { return undefined; } const item = this.items[this.head]; delete this.items[this.head]; this.head++; return item; }
peek(): T | undefined { return this.items[this.head]; }
isEmpty(): boolean { return this.tail === this.head; }
size(): number { return this.tail - this.head; }}الآن عمليات enqueue و dequeue تتم في O(1) - وقت ثابت، بغض النظر عن