المكررات (Iterators) والمولدات (Generators) غير المتزامنة في جافاسكربت
تعتبر جافاسكربت واحدة من أكثر لغات البرمجة شهرة في تطوير تطبيقات الويب، ويعود ذلك إلى قدرتها على التعامل مع العديد من المهام بطريقة فعالة، خاصة عند التعامل مع البيانات وتكرارها. من بين المفاهيم المتقدمة التي تسهل العمل مع البيانات في جافاسكربت، يأتي مفهوم المكررات (Iterators) والمولدات (Generators) غير المتزامنة. هذه الأدوات توفر طريقة مرنة وسهلة لمعالجة مجموعات كبيرة من البيانات بشكل متسلسل دون الحاجة إلى تحميل كافة البيانات دفعة واحدة. في هذا المقال، سنستعرض مفهوم هذه الأدوات، كيفية استخدامها، والتفاصيل التي تميزها في جافاسكربت.
أولاً: المكررات في جافاسكربت
المكرر (Iterator) هو كائن يمكنه المرور على العناصر داخل مجموعة بيانات بشكل تسلسلي. في جافاسكربت، يعتبر المكرر أداة مفيدة للسماح بمرور تسلسلي عبر العناصر دون الحاجة إلى استخدام الحلقات التقليدية. ببساطة، المكرر يتضمن واجهتين رئيسيتين هما:
-
next(): هذه الوظيفة تقوم بإرجاع العنصر التالي في مجموعة البيانات. -
done: خاصية تشير إلى ما إذا كان هناك مزيد من العناصر لتكرارها.
كيفية استخدام المكررات
تقدم جافاسكربت المكررات في العديد من الهياكل مثل المصفوفات (Arrays) والكائنات (Objects) وحتى السلاسل (Strings). عند تطبيق المكرر على مجموعة معينة، يمكن الحصول على عناصر تلك المجموعة واحداً تلو الآخر باستخدام وظيفة next(). على سبيل المثال، لنأخذ مثالاً على استخدام المكررات مع المصفوفات:
javascriptlet array = [10, 20, 30];
let iterator = array[Symbol.iterator]();
console.log(iterator.next()); // { value: 10, done: false }
console.log(iterator.next()); // { value: 20, done: false }
console.log(iterator.next()); // { value: 30, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
في المثال السابق، استخدمنا المكرر على المصفوفة للحصول على العناصر بشكل تسلسلي. كل مرة يتم فيها استدعاء next()، يتم إرجاع العنصر التالي من المصفوفة حتى يتم الوصول إلى نهاية المجموعة.
ثانياً: المولدات في جافاسكربت
المولدات (Generators) هي دالة خاصة في جافاسكربت تسمح بإنشاء مكررات بطريقة مرنة وقابلة للإيقاف. تم تقديمها في ECMAScript 6 (ES6)، وتمكن المولدات المطورين من إنشاء مكررات بشكل أسهل وأكثر مرونة. المولدات لا تُنفذ كل الكود مرة واحدة، بل يمكنها إيقاف التنفيذ في أي نقطة من خلال استخدام كلمة yield وإعادة تشغيلها لاحقاً.
كيفية إنشاء مولد
لإنشاء مولد في جافاسكربت، نستخدم دالة المولد التي تبدأ بالكلمة function*. الفرق الأساسي بين دالة عادية ودالة مولد هو وجود النجمة (*) بعد الكلمة function. علاوة على ذلك، بدلاً من استخدام return، يستخدم المولد الكلمة المفتاحية yield لتمرير القيم بشكل تدرجي.
javascriptfunction* generateNumbers() {
yield 1;
yield 2;
yield 3;
}
let generator = generateNumbers();
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 3, done: false }
console.log(generator.next()); // { value: undefined, done: true }
في هذا المثال، الدالة generateNumbers هي مولد، تقوم بتوليد القيم واحدة تلو الأخرى عند استدعاء next(). عند استدعاء next() في المولد، يتم تنفيذ الكود حتى يصل إلى الكلمة yield، وعندها يتوقف التنفيذ مؤقتاً ويُعيد القيمة المحددة.
المولدات مع المعاملات
يمكن للمولدات أيضاً استقبال قيم خارجية عبر معلمة المولد، مما يتيح للمطورين استخدام البيانات التي يتم تمريرها أثناء سير عملية التكرار. إليك مثالاً يوضح ذلك:
javascriptfunction* greet(name) {
yield `Hello, ${name}`;
yield `How are you, ${name}?`;
}
let greeter = greet('John');
console.log(greeter.next()); // { value: 'Hello, John', done: false }
console.log(greeter.next()); // { value: 'How are you, John?', done: false }
ثالثاً: المولدات غير المتزامنة في جافاسكربت
المولدات غير المتزامنة (Asynchronous Generators) هي ميزة تم إضافتها في ES2018، وهي نوع من المولدات التي تعمل مع العمليات غير المتزامنة مثل استدعاءات الشبكة (API calls) أو أي عملية تتطلب وقتاً طويلاً للتنفيذ. المولدات غير المتزامنة تعمل بنفس طريقة المولدات العادية، ولكن بدلاً من استخدام yield مباشرة، يتم استخدام yield مع await لتحصل على القيم من العمليات غير المتزامنة.
كيفية استخدام المولدات غير المتزامنة
عند استخدام المولدات غير المتزامنة، تحتاج إلى أن تكون الدالة مولداً غير متزامن باستخدام الكلمة async مع الكلمة function* معاً. داخل هذه الدالة، يتم استخدام الكلمة await لتحميل البيانات أو إجراء العمليات غير المتزامنة قبل أن تُعيد القيم باستخدام yield.
javascriptasync function* fetchData() {
let response = await fetch('https://api.example.com/data');
let data = await response.json();
yield data;
}
let dataFetcher = fetchData();
dataFetcher.next().then(result => console.log(result));
في المثال السابق، fetchData هو مولد غير متزامن يقوم بانتظار استجابة من واجهة برمجة التطبيقات (API) باستخدام await، ومن ثم يمرر البيانات التي تم الحصول عليها باستخدام yield.
الفرق بين المولدات المتزامنة وغير المتزامنة
من أبرز الفروقات بين المولدات المتزامنة وغير المتزامنة هو أن المولدات المتزامنة تتعامل مع القيم مباشرة أثناء مرورها عبر المكرر، بينما المولدات غير المتزامنة تعالج البيانات التي يتم جلبها من مصادر تتطلب وقتاً طويلاً، مثل استعلامات قواعد البيانات أو استدعاءات API.
رابعاً: تطبيقات عملية للمولدات غير المتزامنة
تعتبر المولدات غير المتزامنة أداة قوية في جافاسكربت، خاصة عند التعامل مع العمليات التي تأخذ وقتاً طويلاً أو التي تعتمد على الشبكة. إليك بعض الاستخدامات العملية التي يمكن أن تستفيد من المولدات غير المتزامنة:
-
تدفق البيانات من واجهات برمجة التطبيقات (APIs): عندما تحتاج إلى جلب بيانات من واجهات برمجة التطبيقات بشكل تدرجي، يمكن استخدام المولدات غير المتزامنة لجلب البيانات في دفعات دون الحاجة إلى تحميل كافة البيانات دفعة واحدة.
-
التعامل مع ملفات كبيرة: عند التعامل مع ملفات ضخمة، يمكن استخدام المولدات غير المتزامنة لتحميل البيانات بشكل تدريجي، مما يوفر استهلاك الذاكرة ويجعل البرنامج أكثر كفاءة.
-
التزامن في العمليات الشبكية: المولدات غير المتزامنة تسهل كتابة الكود الذي يتعامل مع العمليات المتوازية مثل إرسال واستقبال البيانات من الخوادم، دون الحاجة إلى استخدام الوعود (Promises) أو الدوال العكسية (Callbacks) التقليدية.
خامساً: المولدات غير المتزامنة ومكتبات جافاسكربت
يتم دمج المولدات غير المتزامنة بشكل متزايد في العديد من مكتبات جافاسكربت الحديثة. على سبيل المثال، تستخدم العديد من مكتبات البرمجة التفاعلية مثل Redux-Saga المولدات غير المتزامنة لإدارة تدفق العمليات غير المتزامنة. هذه المكتبات توفر بيئة مناسبة لتنفيذ العمليات غير المتزامنة بشكل سهل، حيث يمكن استخدام المولدات بشكل طبيعي لاستدعاء العمليات الخارجية وانتظار نتائجها.
سادساً: التحديات والأداء
رغم أن المولدات والمولدات غير المتزامنة توفر ميزات رائعة في جافاسكربت، إلا أن هناك بعض التحديات التي قد تواجه المطورين عند استخدام هذه الأدوات:
-
أداء منخفض في بعض الحالات: قد تكون المولدات غير المتزامنة بطيئة في بعض الحالات مقارنةً بالحلول الأخرى مثل الوعود (Promises) أو الدوال العكسية.
-
التعامل مع الأخطاء: من الصعب أحياناً التعامل مع الأخطاء في المولدات غير المتزامنة، خاصةً إذا كانت هذه الأخطاء تحدث في منتصف عملية التكرار.
-
التعقيد: عند استخدام المولدات غير المتزامنة بشكل معقد، يمكن أن تصبح الشفرة أقل وضوحاً، مما يزيد من صعوبة صيانتها على المدى الطويل.
الخاتمة
المكررات والمولدات غير المتزامنة في جافاسكربت

