8.10. الأصناف القابلة للانتظار¶
8.10.1. ما هو الكائن القابل للانتظار¶
عندما تكتب الكوروتين await x، تسأل اللغة x كيف تنتظره. أي كائن يعرف كيف يجيب يكون قابلًا للانتظار. وهناك نوعان:
كائنات الكوروتين التي تعيدها دوال
async def. استدعاءsend_request()ينتج واحدًا من هذه في كل مرة -- كائن الكوروتين، وليس نتيجة الدالة. كتابةawait send_request()هي ما يشغّل الجسم فعليًا.كائنات صنف يعرّف الطريقة
__await__. إنasync defهي الاختصار للنوع الأول؛ و__await__هي الطريقة اليدوية لصنع النوع الثاني.
تستخدم شيفرة التطبيق النوع الأول افتراضيًا. أما النوع الثاني فيوجد للحالة النادرة التي يجب فيها أن يكون الكائن نفسه هو ما ينتظره المستدعي بـ await، وليس نتيجة استدعاء طريقة عليه.
8.10.2. الشكلان جنبًا إلى جنب¶
المنطق نفسه مكتوبًا ككوروتين وكصنف قابل للانتظار:
import asyncio
# async def -- almost always the right choice
async def yield_then_return(value):
await asyncio.sleep_ms(0)
return value
# awaitable class -- equivalent, rarely written
class YieldThenReturn:
def __init__(self, value):
self._value = value
def __await__(self):
yield # let the loop run once
return self._value # value of the ``await`` expression
يمكن انتظار كليهما بـ await بالطريقة نفسها:
async def main():
a = await yield_then_return(1)
b = await YieldThenReturn(2)
print(a, b) # prints: 1 2
الشكل async def أقصر، ويُقرأ مثل Python العادية، ويترك للغة إدارة كل مسك دفاتر التعليق والاستئناف. ولا يحصل شكل الصنف على أي شيء إضافي في هذا المثال.
8.10.3. متى يستحق شكل الصنف مكانته¶
عندما يجب أن يكون الكائن شيئًا يتداوله التطبيق -- لا دالةً يستدعيها المستدعي للحصول على كوروتين -- يكون شكل الصنف هو الخيار الوحيد:
قيمة شبيهة بالمستقبل ينشئها التطبيق ويسلّمها لمنتِج وينتظرها بـ
awaitلاحقًا لاسترداد النتيجة المنتَجة.بدائية مخصّصة مبنية فوق أخرى موجودة حيث تكون الواجهة البرمجية الطبيعية
await my_thingبدلًا منawait my_thing.wait(). وصنفasyncio.Taskنفسه مثال مدمج -- إذ تنجح كتابةawait taskلأنTaskيعرّف__await__.
بالنسبة لأي شيء يناسب الشكل استدعِ دالة، احصل على كوروتين، انتظرها، يفوز async def.
8.10.4. تفاصيل البروتوكول¶
__await__ هي طريقة عادية (وليست async def) تعيد مكرِّرًا. وكتابتها كمولّد -- بتضمين yield واحدة على الأقل في الجسم -- يجعلها كذلك تلقائيًا. كل yield تعلّق الكوروتين التي تنتظر الكائن بـ await وتعيد التحكم إلى حلقة الأحداث؛ ويستأنف المولّد في المرة التالية التي تجدول فيها الحلقة المهمة. أما return المجردة (أو الوصول إلى النهاية) فتُنهي الانتظار؛ وأيًا كان ما تنتجه return فإنه يصبح قيمة تعبير await.
في الممارسة العملية، فإن الصنف النادر الذي يفعل ذلك يفوّض التنازل إلى كائن قابل للانتظار موجود بدلًا من قيادة الحلقة بنفسه:
class WaitForEvent:
def __init__(self, event):
self._event = event
def __await__(self):
yield from self._event.wait().__await__()
return self._event
إن yield from تعيد استخدام __await__ الخاصة بالحدث الأساسي للتعليق الفعلي. ومعظم تطبيقات asyncio لا تحتاج أبدًا إلى كتابة أي من الشكلين.