سطح مقدماتی (Beginner Level)
برنامهنویسی ناهمگام (Asynchronous) یکی از مفاهیم قدرتمند در پایتون مدرن است که به شما اجازه میدهد کارهایی که زمانبر هستند (مثل درخواستهای شبکه یا خواندن فایل) را بدون متوقف کردن کل برنامه انجام دهید.
مفهوم همگام (Sync) در برابر ناهمگام (Async)
در برنامهنویسی معمولی (Synchronous)، کدها خط به خط اجرا میشوند. اگر یک خط ۱۰ ثانیه طول بکشد، کل برنامه ۱۰ ثانیه متوقف میشود. اما در Asynchronous، برنامه میتواند در حین انتظار برای آن ۱۰ ثانیه، کارهای دیگری انجام دهد.
تصور کنید در یک کافیشاپ هستید:
- روش همگام (Sync): صندوقدار سفارش شما را میگیرد، صبر میکند تا قهوه آماده شود، آن را به شما میدهد و سپس سراغ مشتری بعدی میرود.
- روش ناهمگام (Async): صندوقدار سفارش شما را میگیرد، آن را به آشپزخانه میدهد و بلافاصله سفارش نفر بعدی را میگیرد. وقتی قهوه آماده شد، به شما تحویل داده میشود.
تعریف Coroutine با async و await
برای ساخت یک تابع ناهمگام، از کلمه کلیدی async def استفاده میکنیم. برای اجرا کردن و منتظر ماندن نتیجه آن، از await استفاده میکنیم.
نکته مهم: توابع async را نمیتوان مثل توابع معمولی صدا زد؛ آنها باید توسط یک "اجراکننده" مثل asyncio.run() مدیریت شوند.
# مثال ۲: ساختار کلی (Static Snippet)
async def fetch_data():
data = await download_from_server() # صبر میکند اما برنامه قفل نمیشود
return data
اجرای پشت سر هم (Sequential) در Async
حتی در حالت Async، اگر از await پشت سر هم استفاده کنید، کدها نوبت به نوبت اجرا میشوند. قدرت واقعی زمانی است که کارها را همزمان کنیم (که در بخش پیشرفته میبینیم).
سطح پیشرفته (Professional Level)
در این سطح به مدیریت همزمانی واقعی (Concurrency)، کار با Gather، مدیریت خطاها و نکات پرفرمنس در Asyncio میپردازیم.
اجرای همزمان با asyncio.gather
برای اینکه چندین کار واقعاً به صورت همزمان (Concurrent) جلو بروند، باید آنها را با هم زمانبندی کنیم. تابع asyncio.gather به ما اجازه میدهد چندین Coroutine را همزمان استارت بزنیم.
مدیریت Timeout (محدودیت زمانی)
گاهی اوقات یک عملیات شبکه ممکن است خیلی طول بکشد. در برنامههای حرفهای، حتماً باید از asyncio.wait_for برای جلوگیری از فریز شدن روی یک تسک استفاده کنید.
اشتباه رایج: Blocking Code
یکی از بزرگترین اشتباهات در برنامهنویسی Async، استفاده از توابع مسدودکننده (Blocking) مثل time.sleep() یا درخواستهای سنگین CPU داخل یک تابع async است. این کار کل Event Loop را متوقف میکند.
# مثال اشتباه (Static - Do Not Run in Prod)
import time
async def bad_coroutine():
# این خط غلط است! کل برنامه را فریز میکند
time.sleep(5)
print("این کار غلط بود")
# راه حل درست:
# استفاده از await asyncio.sleep(5)
# یا اجرای کارهای سنگین CPU در یک Thread جداگانه با run_in_executor
اجرای Task در پسزمینه (Fire and Forget)
گاهی میخواهیم یک تسک شروع شود اما منتظر نتیجه آن نمانیم (مثلاً ارسال لاگ یا ایمیل). برای این کار از asyncio.create_task استفاده میکنیم.