לולאת האירועים היא הליבה של asyncio.
זו לולאה אינסופית שמנהלת סט של משימות (קורוטינות, Futures, callbacks), ודואגת להריץ כל אחת כשהיא מוכנה.
כאשר קורוטינה מבצעת await על פעולה (למשל sleep או I/O), הלולאה יודעת שהמשימה מחכה, ויכולה להריץ משימות אחרות meantime.
Futures / Promises
ב־asyncio, Future הוא אובייקט שמייצג תוצאה שעדיין לא זמינה.
בדומה ל־Promise ב־JavaScript.
לרוב לא יוצרים Future בעצמכם – אלא משתמשים במבנים ברמה גבוהה יותר (כמו create_task שמחזיר Task – תת־מחלקה של Future).
הפונקציה gather מריצה את כל הקורוטינות במקביל, ומחזירה את כל התוצאות ברשימה אחת לפי הסדר.
השימוש ב־asyncio.gather מתאים כאשר רוצים לבצע מספר פעולות I/O במקביל ולחכות שכולן יסתיימו.
זה יריץ את כל הקורוטינות של fetch_data כמעט בו זמנית.
כל אחת תישן למשך n שניות, כלומר: אחת תישן 0 שניות, השנייה 1, וכן הלאה עד 4.
הזמן הכולל להרצה יהיה כ-4 שניות (הקורוטינה האיטית ביותר), ולאחר מכן תודפס רשימת התוצאות.
gather מחזירה את התוצאות לפי הסדר שבו הועברו הקורוטינות.
ביטול משימות
אם משימה עלולה לרוץ זמן רב, אפשר לבטל אותה עם task.cancel().
זה יזרוק חריגת CancelledError בנקודת ההמתנה (await) הבאה בתוך הקורוטינה.
ניתן ללכוד את asyncio.CancelledError כדי לבצע פעולות ניקוי (cleanup) אם נדרש.
מנהלי הקשר ואיטרטורים אסינכרוניים
פייתון תומכת גם ב־async with ו־async for באמצעות מימוש של __aenter__, __aexit__, ו־__anext__.
זה שימושי כאשר עובדים עם משאבים אסינכרוניים – לדוגמה, פתיחת חיבור לרשת או למסד נתונים בצורה אסינכרונית.
מתי כדאי להשתמש ב־asyncio
כאשר יש הרבה פעולות I/O מקבילות (קריאות לרשת, קבצים, מסדי נתונים וכו’).
במקרים כאלה, asyncio מאפשר טיפול באלפי משימות בו זמנית מבלי הצורך ביצירת חוטים (threads), דבר שחוסך משאבים.
החיסרון: הקוד הופך להיות אסינכרוני לחלוטין – כל פונקציה צריכה להיות async, והקריאות הן await.
משימות שדורשות כוח עיבוד (CPU-bound) אינן מתאימות ל־async – כי כל הקוד עדיין רץ בלולאה אחת.
במקרים כאלה, אפשר לשלב עם multiprocessing או להעביר משימות לבריכת חוטים עם loop.run_in_executor.