بانتظار مستقبل Async Await (الجزء 2) - Perl

نظرة أعمق على Future::AsyncAwait، إلى أين سيتجه بعد ذلك وكيف ينبغي علينا استخدامه.
في الجزء السابق، قدمت وحدة Future::AsyncAwait التي تضيف الزوج من الكلمات المفتاحية async و await. تساعدنا هذه الكلمات على كتابة العديد من أشكال الكود التي تستخدم futures للسلوك غير المتزامن.
تفاصيل داخلية للتنفيذ
كنظرة عامة موجزة، تنقسم الأجزاء الداخلية لوحدة Future::AsyncAwait إلى ثلاثة أجزاء رئيسية:
- تعليق واستئناف وظيفة CV (دالة Perl) قيد التشغيل
- محلل الكلمات المفتاحية للتعرف على كلمتي async و await
- منطق الربط الذي يربط هذين الجزأين مع كائنات Future
من بين هذه الأجزاء الثلاثة، الأول هو الأكبر والأكثر تعقيدًا بكثير، وهو المكان الذي يحدث فيه الكثير من إصلاحات الأخطاء الجارية وتحسين الميزات. يشكل تحليل الكلمات المفتاحية ومنطق الربط مع Future مساهمة صغيرة نسبيًا في حجم الكود الكلي، وقد ثبت استقرارهما جيدًا خلال العام الماضي تقريبًا.
السبب الرئيسي في أن وظيفة التعليق والاستئناف كبيرة ومعقدة هو وجود الكثير من الحالات في أماكن كثيرة من مفسر Perl والتي يجب حفظها عند تعليق دالة قيد التشغيل، ثم إعادتها إلى المكان الصحيح عند استئنافها. جزء كبير من الوقت الذي استغرقه تطويره حتى الآن كان من خلال البحث في التفاصيل الداخلية لمترجم Perl لإيجاد كل هذه التفاصيل، والعمل على كيفية إدارته.
عندما يحتاج دالة قيد التشغيل إلى التعليق عند تعبير await، تُتخذ الخطوات التالية:
- إنشاء قيمة مرجعية CODE جديدة لتمثيل استئناف الدالة عند هذه النقطة.
- التقاط وحفظ أكبر قدر ممكن من حالة الدالة الجارية - كتل النطاق المتداخلة، وقيم المتغيرات المعجمية، والقيم المؤقتة على المكدس، وما إلى ذلك... يتم إرفاق هذه الحالة بمرجعية CODE الجديدة.
- ادفع هذه المرجعية البرمجية كـ -> on_ready رد نداء لـ Future الذي ننتظره.
- إنشاء نسخة Future معلقة جديدة للعودة إلى المنادي.
- اجعل استدعاء async sub يعيد هذه النسخة كنتيجته.
في هذه المرحلة، تم تعليق الدالة الجارية، وتم التقاط حالتها بواسطة مرجعية CODE المخزنة في النسخة Future التي تنتظرها. عندما تصبح تلك النسخة Future جاهزة، تستدعي ردود النداء -> on_ready الخاصة بها بالطريقة المعتادة، ومن بينها مرجعية CODE التي خزناها هناك. ثم تتخذ الخطوات التالية:
- استعادة حالة الدالة قيد التشغيل من القيم التي تم التقاطها أثناء عملية التعليق. يجب أن تستعيد كتل النطاق المتداخلة والمتغيرات المعجمية وما إلى ذلك، بترتيب عكسي عن كيف تم التقاطها.
- استدعاء طريقة -> get على نسخة Future التي كنا ننتظرها، لجلب نتيجتها كقيمة لتعبير await.
في هذه المرحلة، أصبح تعبير await منتهيًا الآن، ويمكن ببساطة استئناف الدالة التي تم استعادتها كالمعتاد.
الأخطاء المعروفة
كما ذُكر سابقًا، الوحدة ليست جاهزة بالكامل للإنتاج بعد لأنها معروفة بوجود بعض القضايا، ومن المحتمل أن تكون هناك المزيد التي لم تُكشف بعد. كنظرة عامة على حالة الاستقرار الحالية، ولتوضيح حجم وأهمية القضايا المعروفة حاليًا، إليكم بعضًا من أهمها:
عناصر تحكم الحلقات الموسومة لا تعمل
(https://rt.cpan.org/Public/Bug/Display.html?id=128205)
تعمل حلقات foreach و while البسيطة بشكل جيد، ولكن استخدام التسميات مع كلمات التحكم في الحلقة (next، last، redo) يؤدي حاليًا إلى فشل. على سبيل المثال:

فشل البحث عن التسمية هنا، مع

الحل الحالي لهذه المشكلة هو ببساطة عدم استخدام عناصر تحكم الحلقات الموسومة عبر حدود await.
تعديل: تم إصلاح هذا الخطأ في الإصدار 0.22.
التعبيرات المعقدة في foreach تفقد القيم
(https://rt.cpan.org/Public/Bug/Display.html?id=128619)
لم أتمكن بعد من عزل حالة اختبار حد أدنى لهذا الخطأ، ولكن جوهر المشكلة هو أنه مع وجود كود يقوم بـ

تُفقد القيمة النهائية 0. تُنفذ الحلقة $len - 1 مرة مع تعيين $value إلى 1، لكنها تفوّت الحالة النهائية 0.
الحل الحالي لهذه المشكلة هو حساب مجموعة القيم الكاملة للحلقة في متغير مصفوفة، ثم تنفيذ foreach على هذه المصفوفة:

بينما يُعتبر هذا حلًا سهلاً، إلا أن وجود هذا الخطأ لا يزال مقلقًا بعض الشيء، لأنه يوضح إمكانية حدوث فشل صامت. الكود لا يسبب رسالة خطأ أو حادثة، بل ينتج فقط نتيجة خاطئة دون أي تحذير أو إشعار بحدوث خطأ. هو، حتى وقت كتابة هذا النص، الخطأ الوحيد المعروف من هذا النوع. كل الأخطاء الأخرى تُنتج رسالة خطأ، وغالبًا ما تحدث حادثة، إما أثناء التحويل أو وقت التشغيل.
تعديل: تم إصلاح هذا الخطأ في الإصدار 0.23.
الاتجاهات المستقبلية
بعيدًا عن إصلاح الأخطاء المعروفة، وبعضها موضح أعلاه، هناك بعض الميزات الناقصة أو التفاصيل الأخرى التي ينبغي معالجتها في وقت قريب.
تكامل Perl الأساسي
حاليًا، تعمل الوحدة كموديول تابع لجهة خارجية عبر CPAN، بدون دعم محدد من نواة Perl. بينما يدرك الحاملون لـ perl5 ("p5p") ويشجعون عمومًا استمرار هذا العمل، لا يوجد تكامل محدد على مستوى الكود للمساعدة المباشرة. هناك تفصيلان محددان أرغب برؤيتهما:
- دعم أساسي أفضل لتحليل وبناء جزء optree المتعلق بتوقيع تعريف دالة الفرعية. حاليًا، لا يمكن لتعريفات async sub استخدام توقيعات الدوال، لأن المحلل ليس دقيقًا بما فيه الكفاية للسماح بذلك. واجهة في نواة Perl لدعم أفضل لذلك ستمكن async subs من أخذ التوقيعات، كما يمكن للنسخ غير الـ async العادية.
- خطة نهائية لترحيل أجزاء من منطق التعليق والاستئناف خارج هذه الوحدة وإلى النواة. أو، على الأقل، طريقة لمحاولة جعلها أكثر قدرة على مجابهة المستقبل. حاليًا، التنفيذ يعتمد بشكل كبير على الإصدارات ويجب أن يفحص ويعمل على العديد من الأجزاء الداخلية المختلفة لمفسر Perl. إذا تمكنت نواة Perl من توفير طريقة لتعليق واستئناف CV قيد التشغيل، فإن ذلك سيجعل Future::AsyncAwait أبسط بكثير وأكثر استقرارًا عبر الإصدارات، كما سيمهد الطريق لوحدات CPAN أخرى لتوفير بناء جمل أو دلالات مختلفة تعتمد على هذا المفهوم، مثل coroutines أو generators.
local و await
حاليًا، منطق التعليق سيُزعج من أي تعديلات على متغيرات local التي تقع ضمن النطاق حينما يحتاج لتعليق الدالة؛ على سبيل المثال

هذا أكثر من مجرد حدّ في التنفيذ؛ فهو يمتد إلى أسئلة أساسية حول المعنى الدلالي لمثل هذا الكود. من الصعب رسم موازٍ لأي من اللغات الأخرى التي ألهمتها بناء جملة async/await، لأن لا واحدة منها تحتوي على بناء مشابه لـ local في Perl.
توصيات للاستخدام
في وقت سابق، ذكرت أن Future::AsyncAwait ليس جاهزًا تمامًا للإنتاج بعد، بسبب عدد قليل من الأخطاء المتبقية بالإضافة إلى نقص عام في اختبارات الإنتاج على نطاق واسع. بينما ربما لا ينبغي استخدامه في مناطق حيوية للأعمال في الوقت الحالي، إلا أنه يمكن أن يساعد بالتأكيد في العديد من المناطق الأخرى.
يكتب توم مولسوورث أنه يجب تجنبه في الجزء الحرج الذي يحقق الربح من كود الإنتاج، ولكن يجب النظر فيه حالياً على أساس كل حالة على حدة بالنسبة للميزات الجديدة، ويجب بالتأكيد أن يؤخذ في الاعتبار للاختبارات الوحدوية. ويضيف:
يجب أن يحتوي أي كود يستخدمه على تغطية اختبار جيدة، ويخضع أيضًا لاختبارات التحميل. [...] الكود البسيط والقابل للقراءة سيكون فائدة قد تفوق المخاطر المحتملة لاستخدام وحدات أحدث وأقل اختبارًا مثل هذه.
هذه النصيحة مماثلة لاستخدامي الشخصي للوحدة، والذي يقتصر حاليًا على مجموعة قليلة من وحدات CPAN الخاصة بي التي تتعلق بأجزاء غريبة من الأجهزة.
أجد أن النظافة العامة والتعبيرية لاستخدام تعبيرات async/await هي مبرر سهل مقابل إمكانية وجود مشكلات في هذه المجالات. مع إصلاح الأخطاء واكتشاف أن الوحدة أصبحت أكثر استقرارًا وموثوقية، يمكن دفع الحدود إلى أبعد، وإدخال الوحدة في المزيد من الأماكن.
ملاحظة: تم نقل هذا المنشور من https://tech.binary.com/ (مدونتنا التقنية القديمة). مؤلف هذه المقالة هو https://metacpan.org/author/PEVANS