committed by
GitHub
1 changed files with 444 additions and 0 deletions
@ -0,0 +1,444 @@ |
|||
# همزمانی و async / await |
|||
|
|||
جزئیات در مورد سینتکس `async def` برای *توابع عملیات مسیر* و یه کم پیشزمینه در مورد کد ناهمزمان، همزمانی و موازیسازی. |
|||
|
|||
## عجله داری؟ |
|||
|
|||
<abbr title="خیلی طولانی بود؛ نخوندم"><strong>TL;DR:</strong></abbr> |
|||
|
|||
اگه از کتابخونههای سومشخصی استفاده میکنی که بهت میگن با `await` صداشون کنی، مثل: |
|||
|
|||
```Python |
|||
results = await some_library() |
|||
``` |
|||
|
|||
اون وقت، *توابع عملیات مسیرت* رو با `async def` تعریف کن، اینجوری: |
|||
|
|||
```Python hl_lines="2" |
|||
@app.get('/') |
|||
async def read_results(): |
|||
results = await some_library() |
|||
return results |
|||
``` |
|||
|
|||
/// note |
|||
|
|||
فقط توی توابعی که با `async def` ساخته شدن میتونی از `await` استفاده کنی. |
|||
|
|||
/// |
|||
|
|||
--- |
|||
|
|||
اگه از یه کتابخونه سومشخص استفاده میکنی که با یه چیزی (مثل دیتابیس، API، سیستم فایل و غیره) ارتباط داره و از `await` پشتیبانی نمیکنه (که الان برای بیشتر کتابخونههای دیتابیس اینجوریه)، اون وقت *توابع عملیات مسیرت* رو عادی، فقط با `def` تعریف کن، اینجوری: |
|||
|
|||
```Python hl_lines="2" |
|||
@app.get('/') |
|||
def results(): |
|||
results = some_library() |
|||
return results |
|||
``` |
|||
|
|||
--- |
|||
|
|||
اگه برنامهات (به هر دلیلی) لازم نیست با چیز دیگهای ارتباط برقرار کنه و منتظر جوابش بمونه، از `async def` استفاده کن. |
|||
|
|||
--- |
|||
|
|||
اگه نمیدونی چیکار کنی، از `def` معمولی استفاده کن. |
|||
|
|||
--- |
|||
|
|||
**توجه**: میتونی توی *توابع عملیات مسیرت* هر چقدر که لازم داری `def` و `async def` رو قاطی کنی و هر کدوم رو با بهترین گزینه برات تعریف کنی. FastAPI خودش کار درست رو باهاشون انجام میده. |
|||
|
|||
به هر حال، توی هر کدوم از موقعیتهای بالا، FastAPI هنوز ناهمزمان کار میکنه و خیلی خیلی سریع هست. |
|||
|
|||
ولی با دنبال کردن مراحل بالا، میتونه یه سری بهینهسازی عملکرد هم بکنه. |
|||
|
|||
## جزئیات فنی |
|||
|
|||
نسخههای مدرن پایتون از **"کد ناهمزمان"** با چیزی که بهش **"کروتین"** میگن پشتیبانی میکنن، با سینتکس **`async` و `await`**. |
|||
|
|||
بیاید این جمله رو تکهتکه توی بخشهای زیر ببینیم: |
|||
|
|||
* **کد ناهمزمان** |
|||
* **`async` و `await`** |
|||
* **کروتینها** |
|||
|
|||
## کد ناهمزمان |
|||
|
|||
کد ناهمزمان یعنی زبون 💬 یه راهی داره که به کامپیوتر / برنامه 🤖 بگه توی یه جای کد، باید منتظر بمونه تا *یه چیز دیگه* یه جای دیگه تموم بشه. فرض کن اون *یه چیز دیگه* اسمش "فایل-آروم" 📝 باشه. |
|||
|
|||
پس، توی اون مدت، کامپیوتر میتونه بره یه کار دیگه بکنه، تا وقتی "فایل-آروم" 📝 تموم بشه. |
|||
|
|||
بعدش کامپیوتر / برنامه 🤖 هر وقت فرصتی داشته باشه برمیگرده، چون دوباره منتظره، یا هر وقت همه کاری که اون لحظه داشته تموم کرده. و میبینه آیا کارایی که منتظرشون بوده تموم شدن یا نه، و هر کاری که باید بکنه رو انجام میده. |
|||
|
|||
بعد، اون 🤖 اولین کاری که تموم شده (مثلاً "فایل-آروم" 📝 ما) رو برمیداره و هر کاری که باید باهاش بکنه رو ادامه میده. |
|||
|
|||
این "منتظر یه چیز دیگه بودن" معمولاً به عملیات <abbr title="ورودی و خروجی">I/O</abbr> اشاره داره که نسبتاً "آروم" هستن (نسبت به سرعت پردازنده و حافظه RAM)، مثل منتظر موندن برای: |
|||
|
|||
* دادههایی که از کلاینت از طریق شبکه فرستاده میشن |
|||
* دادههایی که برنامهات فرستاده تا از طریق شبکه به کلاینت برسه |
|||
* محتوای یه فایل توی دیسک که سیستم بخوندش و به برنامهات بده |
|||
* محتوایی که برنامهات به سیستم داده تا توی دیسک بنویسه |
|||
* یه عملیات API از راه دور |
|||
* یه عملیات دیتابیس که تموم بشه |
|||
* یه کوئری دیتابیس که نتایجش برگرده |
|||
* و غیره. |
|||
|
|||
چون زمان اجرا بیشتر صرف انتظار برای عملیات <abbr title="ورودی و خروجی">I/O</abbr> میشه، بهشون میگن عملیات "I/O bound". |
|||
|
|||
بهش "ناهمزمان" میگن چون کامپیوتر / برنامه لازم نیست با کار آروم "همزمان" باشه، منتظر لحظه دقیق تموم شدن کار بمونه، در حالی که هیچ کاری نمیکنه، تا نتیجه رو بگیره و کارش رو ادامه بده. |
|||
|
|||
به جاش، چون یه سیستم "ناهمزمان" هست، وقتی کار تموم شد، میتونه یه کم توی صف منتظر بمونه (چند میکروثانیه) تا کامپیوتر / برنامه هر کاری که رفته بکنه رو تموم کنه، و بعد برگرده نتیجه رو بگیره و باهاش کار کنه. |
|||
|
|||
برای "همزمان" (برخلاف "ناهمزمان") معمولاً از اصطلاح "ترتیبی" هم استفاده میکنن، چون کامپیوتر / برنامه همه مراحل رو به ترتیب دنبال میکنه قبل از اینکه بره سراغ یه کار دیگه، حتی اگه اون مراحل شامل انتظار باشن. |
|||
|
|||
### همزمانی و برگرها |
|||
|
|||
این ایده **ناهمزمان** که بالا توضیح دادم گاهی بهش **"همزمانی"** هم میگن. با **"موازیسازی"** فرق داره. |
|||
|
|||
**همزمانی** و **موازیسازی** هر دو به "اتفاق افتادن چیزای مختلف کموبیش همزمان" ربط دارن. |
|||
|
|||
ولی جزئیات بین *همزمانی* و *موازیسازی* خیلی متفاوته. |
|||
|
|||
برای دیدن فرقش، این داستان در مورد برگرها رو تصور کن: |
|||
|
|||
### برگرهای همزمان |
|||
|
|||
با عشقت میری فستفود بگیرین، توی صف وایمیستی در حالی که صندوقدار سفارش آدمای جلوی تو رو میگیره. 😍 |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-01.png" class="illustration"> |
|||
|
|||
بعد نوبت تو میشه، سفارش دو تا برگر خیلی شیک برای خودت و عشقت میدی. 🍔🍔 |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-02.png" class="illustration"> |
|||
|
|||
صندوقدار یه چیزی به آشپز توی آشپزخونه میگه تا بدونن باید برگرهای تو رو آماده کنن (گرچه الان دارن برگرهای مشتریای قبلی رو درست میکنن). |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-03.png" class="illustration"> |
|||
|
|||
پول رو میدی. 💸 |
|||
|
|||
صندوقدار شماره نوبتت رو بهت میده. |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-04.png" class="illustration"> |
|||
|
|||
وقتی منتظری، با عشقت میری یه میز انتخاب میکنی، میشینی و کلی با عشقت حرف میزنی (چون برگرهات خیلی شیکن و آماده کردنشون یه کم طول میکشه). |
|||
|
|||
وقتی پشت میز با عشقت نشستی، در حالی که منتظر برگرهایی، میتونی اون زمان رو صرف تحسین این کنی که عشقت چقدر باحال، ناز و باهوشه ✨😍✨. |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-05.png" class="illustration"> |
|||
|
|||
وقتی منتظری و با عشقت حرف میزنی، هر از گاهی شمارهای که رو پیشخون نشون داده میشه رو چک میکنی که ببینی نوبتت شده یا نه. |
|||
|
|||
بعد یه جایی بالاخره نوبتت میشه. میری پیشخون، برگرهات رو میگیری و برمیگردی سر میز. |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-06.png" class="illustration"> |
|||
|
|||
تو و عشقت برگرها رو میخورین و یه وقت خوب باهم دارین. ✨ |
|||
|
|||
<img src="/img/async/concurrent-burgers/concurrent-burgers-07.png" class="illustration"> |
|||
|
|||
/// info |
|||
|
|||
تصاویر قشنگ از <a href="https://www.instagram.com/ketrinadrawsalot" class="external-link" target="_blank">کترینا تامپسون</a>. 🎨 |
|||
|
|||
/// |
|||
|
|||
--- |
|||
|
|||
تصور کن تو توی این داستان کامپیوتر / برنامه 🤖 هستی. |
|||
|
|||
وقتی توی صف هستی، فقط بیکاری 😴، منتظر نوبتت هستی، کار خیلی "مفیدی" نمیکنی. ولی صف سریع پیش میره چون صندوقدار فقط سفارش میگیره (آمادشون نمیکنه)، پس این خوبه. |
|||
|
|||
بعد، وقتی نوبتت میشه، کار "مفید" واقعی میکنی، منو رو پردازش میکنی، تصمیم میگیری چی میخوای، انتخاب عشقت رو میگیری، پول میدی، چک میکنی اسکناس یا کارت درست رو دادی، چک میکنی درست حساب شده، چک میکنی سفارش آیتمای درست رو داره و غیره. |
|||
|
|||
ولی بعد، گرچه هنوز برگرهات رو نداری، کارت با صندوقدار "موقتاً متوقف" ⏸ میشه، چون باید منتظر بمونی 🕙 تا برگرهات آماده بشن. |
|||
|
|||
ولی وقتی از پیشخون دور میشی و با شماره نوبتت سر میز میشینی، میتونی توجهت رو 🔀 به عشقت بدی و "کار" ⏯ 🤓 رو اون بکنی. بعدش دوباره داری یه چیز خیلی "مفید" انجام میدی، مثل لاس زدن با عشقت 😍. |
|||
|
|||
بعد صندوقدار 💁 با گذاشتن شمارهات رو نمایشگر پیشخون میگه "من با درست کردن برگرها تموم کردم"، ولی تو مثل دیوونهها وقتی شمارهات رو نمایشگر میاد فوری نمیپری. میدونی کسی برگرهات رو نمیدزده چون شماره نوبتت رو داری، و اونا هم مال خودشون رو دارن. |
|||
|
|||
پس منتظر میمونی تا عشقت داستانش رو تموم کنه (کار فعلی ⏯ / وظیفهای که داره پردازش میشه 🤓)، آروم لبخند میزنی و میگی که میری برگرها رو بیاری ⏸. |
|||
|
|||
بعد میری پیشخون 🔀، به کار اولیه که حالا تموم شده ⏯، برگرها رو میگیری، تشکر میکنی و میبرشون سر میز. این مرحله / وظیفه تعامل با پیشخون رو تموم میکنه ⏹. این به نوبه خودش یه وظیفه جدید، "خوردن برگرها" 🔀 ⏯، میسازه، ولی اون قبلی که "گرفتن برگرها" بود تموم شده ⏹. |
|||
|
|||
### برگرهای موازی |
|||
|
|||
حالا فرض کن اینا "برگرهای همزمان" نیستن، بلکه "برگرهای موازی" هستن. |
|||
|
|||
با عشقت میری فستفود موازی بگیری. |
|||
|
|||
توی صف وایمیستی در حالی که چند تا (مثلاً 8 تا) صندوقدار که همزمان آشپز هم هستن سفارش آدمای جلوی تو رو میگیرن. |
|||
|
|||
همه قبل تو منتظرن برگرهاشون آماده بشه قبل از اینکه پیشخون رو ترک کنن، چون هر کدوم از 8 تا صندوقدار میره و برگر رو همون موقع درست میکنه قبل از اینکه سفارش بعدی رو بگیره. |
|||
|
|||
<img src="/img/async/parallel-burgers/parallel-burgers-01.png" class="illustration"> |
|||
|
|||
بالاخره نوبت تو میشه، سفارش دو تا برگر خیلی شیک برای خودت و عشقت میدی. |
|||
|
|||
پول رو میدی 💸. |
|||
|
|||
<img src="/img/async/parallel-burgers/parallel-burgers-02.png" class="illustration"> |
|||
|
|||
صندوقدار میره آشپزخونه. |
|||
|
|||
منتظر میمونی، جلوی پیشخون وایستادی 🕙، که کسی قبل از تو برگرهات رو نگیره، چون شماره نوبت نیست. |
|||
|
|||
<img src="/img/async/parallel-burgers/parallel-burgers-03.png" class="illustration"> |
|||
|
|||
چون تو و عشقت مشغول این هستین که نذارین کسی جلوتون بیاد و هر وقت برگرها رسیدن اونا رو بگیره، نمیتونی به عشقت توجه کنی. 😞 |
|||
|
|||
این کار "همزمان" هست، تو با صندوقدار/آشپز 👨🍳 "همزمان" هستی. باید منتظر بمونی 🕙 و درست همون لحظه که صندوقدار/آشپز 👨🍳 برگرها رو تموم میکنه و بهت میده اونجا باشی، وگرنه ممکنه یکی دیگه اونا رو بگیره. |
|||
|
|||
<img src="/img/async/parallel-burgers/parallel-burgers-04.png" class="illustration"> |
|||
|
|||
بعد صندوقدار/آشپزت 👨🍳 بالاخره بعد از یه مدت طولانی انتظار 🕙 جلوی پیشخون با برگرهات برمیگرده. |
|||
|
|||
<img src="/img/async/parallel-burgers/parallel-burgers-05.png" class="illustration"> |
|||
|
|||
برگرهات رو میگیری و با عشقت میری سر میز. |
|||
|
|||
فقط میخورینشون، و تمومه. ⏹ |
|||
|
|||
<img src="/img/async/parallel-burgers/parallel-burgers-06.png" class="illustration"> |
|||
|
|||
حرف زدن یا لاس زدن زیاد نبود چون بیشتر وقت صرف انتظار 🕙 جلوی پیشخون شد. 😞 |
|||
|
|||
/// info |
|||
|
|||
تصاویر قشنگ از <a href="https://www.instagram.com/ketrinadrawsalot" class="external-link" target="_blank">کترینا تامپسون</a>. 🎨 |
|||
|
|||
/// |
|||
|
|||
--- |
|||
|
|||
توی این سناریوی برگرهای موازی، تو یه کامپیوتر / برنامه 🤖 با دو تا پردازنده (تو و عشقت) هستی، هر دو منتظر 🕙 و توجهشون ⏯ رو برای مدت طولانی "انتظار جلوی پیشخون" 🕙 گذاشتن. |
|||
|
|||
فستفود 8 تا پردازنده (صندوقدار/آشپز) داره. در حالی که فستفود برگرهای همزمان شاید فقط 2 تا داشته (یه صندوقدار و یه آشپز). |
|||
|
|||
ولی با این حال، تجربه نهایی بهترین نیست. 😞 |
|||
|
|||
--- |
|||
|
|||
این معادل موازی داستان برگرها بود. 🍔 |
|||
|
|||
برای یه مثال "واقعیتر" از زندگی، یه بانک رو تصور کن. |
|||
|
|||
تا همین چند وقت پیش، بیشتر بانکها چند تا صندوقدار 👨💼👨💼👨💼👨💼 داشتن و یه صف بزرگ 🕙🕙🕙🕙🕙🕙🕙🕙. |
|||
|
|||
همه صندوقدارها کار رو با یه مشتری بعد از اون یکی 👨💼⏯ انجام میدادن. |
|||
|
|||
و باید توی صف 🕙 مدت زیادی منتظر بمونی وگرنه نوبتت رو از دست میدی. |
|||
|
|||
احتمالاً نمیخوای عشقت 😍 رو با خودت ببری بانک 🏦 برای کارای روزمره. |
|||
|
|||
### نتیجهگیری برگرها |
|||
|
|||
توی این سناریوی "برگرهای فستفود با عشقت"، چون کلی انتظار 🕙 هست، خیلی منطقیتره که یه سیستم همزمان ⏸🔀⏯ داشته باشی. |
|||
|
|||
این برای بیشتر برنامههای وب هم صدق میکنه. |
|||
|
|||
خیلی خیلی کاربر، ولی سرورت منتظر 🕙 اتصال نهچندان خوبشون هست تا درخواستهاشون رو بفرستن. |
|||
|
|||
و بعد دوباره منتظر 🕙 که جوابها برگردن. |
|||
|
|||
این "انتظار" 🕙 توی میکروثانیهها اندازهگیری میشه، ولی با این حال، جمعش که بکنی آخرش کلی انتظار میشه. |
|||
|
|||
برای همین استفاده از کد ناهمزمان ⏸🔀⏯ برای APIهای وب خیلی منطقیه. |
|||
|
|||
این نوع ناهمزمانی چیزیه که NodeJS رو محبوب کرد (گرچه NodeJS موازی نیست) و نقطه قوت Go بهعنوان یه زبون برنامهنویسیه. |
|||
|
|||
و همون سطح عملکردی هست که با **FastAPI** میگیری. |
|||
|
|||
و چون میتونی همزمانی و موازیسازی رو همزمان داشته باشی، عملکرد بالاتری از بیشتر فریمورکهای تستشده NodeJS میگیری و همتراز با Go، که یه زبون کامپایلشده نزدیک به C هست <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(همه اینا به لطف Starlette)</a>. |
|||
|
|||
### آیا همزمانی از موازیسازی بهتره؟ |
|||
|
|||
نه! این نتیجه داستان نیست. |
|||
|
|||
همزمانی با موازیسازی فرق داره. و توی **سناریوهای خاص** که کلی انتظار دارن بهتره. به همین خاطر، معمولاً برای توسعه برنامههای وب خیلی از موازیسازی بهتره. ولی نه برای همهچیز. |
|||
|
|||
برای اینکه یه تعادل بذاریم، این داستان کوتاه رو تصور کن: |
|||
|
|||
> باید یه خونه بزرگ و کثیف رو تمیز کنی. |
|||
|
|||
*آره، کل داستان همینه*. |
|||
|
|||
--- |
|||
|
|||
هیچ انتظاری 🕙 اونجا نیست، فقط کلی کار برای انجام دادن توی جاهای مختلف خونه. |
|||
|
|||
میتونی مثل مثال برگرها نوبت بذاری، اول پذیرایی، بعد آشپزخونه، ولی چون منتظر چیزی نیستی 🕙، فقط داری تمیز میکنی و تمیز میکنی، نوبتها هیچ تأثیری نداره. |
|||
|
|||
با نوبت یا بدون نوبت (همزمانی) همون قدر طول میکشه تا تمومش کنی و همون مقدار کار رو کردی. |
|||
|
|||
ولی توی این موقعیت، اگه بتونی اون 8 تا صندوقدار/آشپز/حالا-تمیزکار رو بیاری، و هر کدومشون (بهعلاوه خودت) یه قسمت از خونه رو تمیز کنن، میتونی همه کار رو **موازی** انجام بدی، با کمک اضافی، و خیلی زودتر تمومش کنی. |
|||
|
|||
توی این سناریو، هر کدوم از تمیزکارها (از جمله خودت) یه پردازندهست که کار خودش رو میکنه. |
|||
|
|||
و چون بیشتر زمان اجرا صرف کار واقعی میشه (به جای انتظار)، و کار توی کامپیوتر با <abbr title="واحد پردازش مرکزی">CPU</abbr> انجام میشه، به این مشکلات میگن "CPU bound". |
|||
|
|||
--- |
|||
|
|||
مثالهای رایج عملیات CPU bound چیزایی هستن که نیاز به پردازش ریاضی پیچیده دارن. |
|||
|
|||
مثلاً: |
|||
|
|||
* پردازش **صدا** یا **تصویر**. |
|||
* **بینایی کامپیوتری**: یه تصویر از میلیونها پیکسل تشکیل شده، هر پیکسل 3 تا مقدار / رنگ داره، پردازشش معمولاً نیاز داره چیزی رو رو اون پیکسلها همزمان حساب کنی. |
|||
* **یادگیری ماشین**: معمولاً کلی ضرب "ماتریس" و "بردار" لازم داره. یه جدول بزرگ پر از عدد رو تصور کن که همهشون رو همزمان ضرب میکنی. |
|||
* **یادگیری عمیق**: این یه زیرشاخه از یادگیری ماشینه، پس همون قضیه صدق میکنه. فقط این که یه جدول عدد برای ضرب کردن نیست، بلکه یه مجموعه بزرگ از اونا هست، و توی خیلی موارد از یه پردازنده خاص برای ساخت و / یا استفاده از این مدلها استفاده میکنی. |
|||
|
|||
### همزمانی + موازیسازی: وب + یادگیری ماشین |
|||
|
|||
با **FastAPI** میتونی از همزمانی که برای توسعه وب خیلی رایجه (همون جذابیت اصلی NodeJS) استفاده کنی. |
|||
|
|||
ولی میتونی از فواید موازیسازی و چندپردازشی (اجرای چند پروسه بهصورت موازی) برای کارای **CPU bound** مثل سیستمهای یادگیری ماشین هم بهره ببری. |
|||
|
|||
این، بهعلاوه این واقعیت ساده که پایتون زبون اصلی برای **علم داده**، یادگیری ماشین و بهخصوص یادگیری عمیقه، باعث میشه FastAPI یه انتخاب خیلی خوب برای APIها و برنامههای وب علم داده / یادگیری ماشین باشه (بین خیلی چیزای دیگه). |
|||
|
|||
برای دیدن اینکه چطور توی محیط واقعی به این موازیسازی برسی، بخش [استقرار](deployment/index.md){.internal-link target=_blank} رو ببین. |
|||
|
|||
## `async` و `await` |
|||
|
|||
نسخههای مدرن پایتون یه راه خیلی ساده و قابلفهم برای تعریف کد ناهمزمان دارن. این باعث میشه مثل کد "ترتیبی" معمولی به نظر بیاد و توی لحظههای درست "انتظار" رو برات انجام بده. |
|||
|
|||
وقتی یه عملیاتی هست که قبل از دادن نتیجهها نیاز به انتظار داره و از این قابلیتهای جدید پایتون پشتیبانی میکنه، میتونی اینجوری کدنویسیش کنی: |
|||
|
|||
```Python |
|||
burgers = await get_burgers(2) |
|||
``` |
|||
|
|||
نکته کلیدی اینجا `await` هست. به پایتون میگه که باید ⏸ منتظر بمونه تا `get_burgers(2)` کارش 🕙 تموم بشه قبل از اینکه نتیجهها رو توی `burgers` ذخیره کنه. با این، پایتون میدونه که میتونه بره یه کار دیگه 🔀 ⏯ توی این مدت بکنه (مثل گرفتن یه درخواست دیگه). |
|||
|
|||
برای اینکه `await` کار کنه، باید توی یه تابع باشه که از این ناهمزمانی پشتیبانی کنه. برای این کار، فقط با `async def` تعریفش میکنی: |
|||
|
|||
```Python hl_lines="1" |
|||
async def get_burgers(number: int): |
|||
# یه سری کار ناهمزمان برای ساختن برگرها انجام بده |
|||
return burgers |
|||
``` |
|||
|
|||
...به جای `def`: |
|||
|
|||
```Python hl_lines="2" |
|||
# این ناهمزمان نیست |
|||
def get_sequential_burgers(number: int): |
|||
# یه سری کار ترتیبی برای ساختن برگرها انجام بده |
|||
return burgers |
|||
``` |
|||
|
|||
با `async def`، پایتون میدونه که توی اون تابع باید حواسش به عبارتهای `await` باشه، و میتونه اجرای اون تابع رو "موقتاً متوقف" ⏸ کنه و بره یه کار دیگه 🔀 قبل از برگشتن بکنه. |
|||
|
|||
وقتی میخوای یه تابع `async def` رو صدا کنی، باید "منتظرش" بمونی. پس این کار نمیکنه: |
|||
|
|||
```Python |
|||
# این کار نمیکنه، چون get_burgers با async def تعریف شده |
|||
burgers = get_burgers(2) |
|||
``` |
|||
|
|||
--- |
|||
|
|||
پس، اگه از یه کتابخونه استفاده میکنی که بهت میگه میتونی با `await` صداش کنی، باید *توابع عملیات مسیرت* که ازش استفاده میکنن رو با `async def` بسازی، مثل: |
|||
|
|||
```Python hl_lines="2-3" |
|||
@app.get('/burgers') |
|||
async def read_burgers(): |
|||
burgers = await get_burgers(2) |
|||
return burgers |
|||
``` |
|||
|
|||
### جزئیات فنیتر |
|||
|
|||
شاید متوجه شده باشی که `await` فقط توی توابعی که با `async def` تعریف شدن میتونه استفاده بشه. |
|||
|
|||
ولی در عین حال، توابعی که با `async def` تعریف شدن باید "منتظر"شون بمونی. پس توابع با `async def` فقط توی توابعی که با `async def` تعریف شدن میتونن صدا زده بشن. |
|||
|
|||
حالا، قضیه مرغ و تخممرغ چیه، چطور اولین تابع `async` رو صدا میکنی؟ |
|||
|
|||
اگه با **FastAPI** کار میکنی، لازم نیست نگران این باشی، چون اون "اولین" تابع، *تابع عملیات مسیرت* هست، و FastAPI میدونه چطور کار درست رو بکنه. |
|||
|
|||
ولی اگه بخوای بدون FastAPI از `async` / `await` استفاده کنی، اینم ممکنه. |
|||
|
|||
### کد ناهمزمان خودت رو بنویس |
|||
|
|||
Starlette (و **FastAPI**) بر پایه <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> هستن، که باعث میشه با کتابخونه استاندارد پایتون <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> و <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a> سازگار باشه. |
|||
|
|||
بهخصوص، میتونی مستقیماً از <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> برای موارد استفاده پیشرفته همزمانی که نیاز به الگوهای پیچیدهتر توی کد خودت دارن استفاده کنی. |
|||
|
|||
و حتی اگه از FastAPI استفاده نکنی، میتونی برنامههای ناهمزمان خودت رو با <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> بنویسی تا خیلی سازگار باشه و فوایدش رو بگیری (مثل *همزمانی ساختاریافته*). |
|||
|
|||
من یه کتابخونه دیگه روی AnyIO ساختم، یه لایه نازک روش، تا یه کم annotationهای نوع رو بهتر کنم و **تکمیل خودکار** بهتر، **خطاهای درونخطی** و غیره بگیرم. یه مقدمه و آموزش ساده هم داره که بهت کمک میکنه **بفهمی** و **کد ناهمزمان خودت رو بنویسی**: <a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>. اگه بخوای **کد ناهمزمان رو با کد معمولی** (بلاککننده/همزمان) ترکیب کنی خیلی بهدردت میخوره. |
|||
|
|||
### شکلهای دیگه کد ناهمزمان |
|||
|
|||
این سبک استفاده از `async` و `await` توی زبون نسبتاً جدیده. |
|||
|
|||
ولی کار با کد ناهمزمان رو خیلی سادهتر میکنه. |
|||
|
|||
همین سینتکس (یا تقریباً یکسان) اخیراً توی نسخههای مدرن جاوااسکریپت (توی مرورگر و NodeJS) هم اضافه شده. |
|||
|
|||
ولی قبل از اون، مدیریت کد ناهمزمان خیلی پیچیدهتر و سختتر بود. |
|||
|
|||
توی نسخههای قبلی پایتون، میتونستی از نخها یا <a href="https://www.gevent.org/" class="external-link" target="_blank">Gevent</a> استفاده کنی. ولی کد خیلی پیچیدهتر میشه برای فهمیدن، دیباگ کردن و فکر کردن بهش. |
|||
|
|||
توی نسخههای قبلی NodeJS / جاوااسکریپت مرورگر، از "کالبکها" استفاده میکردی. که میرسید به <a href="http://callbackhell.com/" class="external-link" target="_blank">جهان کالبکها</a>. |
|||
|
|||
## کروتینها |
|||
|
|||
**کروتین** فقط یه اصطلاح خیلی شیک برای چیزیه که یه تابع `async def` برمیگردونه. پایتون میدونه که این یه چیزی مثل تابع هست، میتونه شروع بشه و یه جایی تموم بشه، ولی ممکنه داخلش هم موقف ⏸ بشه، هر وقت یه `await` توش باشه. |
|||
|
|||
ولی همه این قابلیت استفاده از کد ناهمزمان با `async` و `await` خیلی وقتا خلاصه میشه به استفاده از "کروتینها". این قابل مقایسه با ویژگی اصلی Go، یعنی "Goroutineها" هست. |
|||
|
|||
## نتیجهگیری |
|||
|
|||
بیاید همون جمله از بالا رو ببینیم: |
|||
|
|||
> نسخههای مدرن پایتون از **"کد ناهمزمان"** با چیزی که بهش **"کروتین"** میگن پشتیبانی میکنن، با سینتکس **`async` و `await`**. |
|||
|
|||
حالا باید بیشتر برات معنی بده. ✨ |
|||
|
|||
همه اینا چیزیه که به FastAPI (از طریق Starlette) قدرت میده و باعث میشه عملکرد چشمگیری داشته باشه. |
|||
|
|||
## جزئیات خیلی فنی |
|||
|
|||
/// warning |
|||
|
|||
احتمالاً میتونی اینو رد کنی. |
|||
|
|||
اینا جزئیات خیلی فنی از نحوه کار **FastAPI** زیر پوستهست. |
|||
|
|||
اگه یه کم دانش فنی (کروتینها، نخها، بلاک کردن و غیره) داری و کنجکاوی که FastAPI چطور `async def` رو در مقابل `def` معمولی مدیریت میکنه، ادامه بده. |
|||
|
|||
/// |
|||
|
|||
### توابع عملیات مسیر |
|||
|
|||
وقتی یه *تابع عملیات مسیر* رو با `def` معمولی به جای `async def` تعریف میکنی، توی یه استخر نخ خارجی اجرا میشه که بعدش منتظرش میمونن، به جای اینکه مستقیم صداش کنن (چون سرور رو بلاک میکنه). |
|||
|
|||
اگه از یه فریمورک ناهمزمان دیگه میای که به روش بالا کار نمیکنه و عادت داری *توابع عملیات مسیر* ساده فقط محاسباتی رو با `def` معمولی برای یه سود کوچیک عملکرد (حدود 100 نانوثانیه) تعریف کنی، توجه کن که توی **FastAPI** اثرش کاملاً برعکسه. توی این موارد، بهتره از `async def` استفاده کنی مگه اینکه *توابع عملیات مسیرت* کدی داشته باشن که عملیات <abbr title="ورودی/خروجی: خوندن یا نوشتن دیسک، ارتباطات شبکه">I/O</abbr> بلاککننده انجام بده. |
|||
|
|||
با این حال، توی هر دو موقعیت، احتمالش زیاده که **FastAPI** هنوز [سریعتر](index.md#performance){.internal-link target=_blank} از فریمورک قبلیات باشه (یا حداقل قابل مقایسه باهاش). |
|||
|
|||
### وابستگیها |
|||
|
|||
همین برای [وابستگیها](tutorial/dependencies/index.md){.internal-link target=_blank} هم صدق میکنه. اگه یه وابستگی یه تابع `def` معمولی به جای `async def` باشه، توی استخر نخ خارجی اجرا میشه. |
|||
|
|||
### زیروابستگیها |
|||
|
|||
میتونی چند تا وابستگی و [زیروابستگی](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} داشته باشی که همدیگه رو نیاز دارن (بهعنوان پارامترهای تعریف تابع)، بعضیهاشون ممکنه با `async def` ساخته بشن و بعضیها با `def` معمولی. بازم کار میکنه، و اونایی که با `def` معمولی ساخته شدن توی یه نخ خارجی (از استخر نخ) صدا زده میشن به جای اینکه "منتظرشون" بمونن. |
|||
|
|||
### توابع کاربردی دیگه |
|||
|
|||
هر تابع کاربردی دیگهای که مستقیم خودت صداش میکنی میتونه با `def` معمولی یا `async def` ساخته بشه و FastAPI رو نحوه صدازدنش تأثیر نمیذاره. |
|||
|
|||
این برخلاف توابعی هست که FastAPI برات صداشون میکنه: *توابع عملیات مسیر* و وابستگیها. |
|||
|
|||
اگه تابع کاربردیت یه تابع معمولی با `def` باشه، مستقیم صداش میکنن (همونطور که توی کدت نوشتی)، نه توی استخر نخ، اگه تابع با `async def` ساخته شده باشه، باید وقتی توی کدت صداش میکنی `await`ش کنی. |
|||
|
|||
--- |
|||
|
|||
دوباره، اینا جزئیات خیلی فنی هستن که احتمالاً اگه دنبالشون اومده باشی برات مفید باشن. |
|||
|
|||
وگرنه، با راهنماییهای بخش بالا باید خوب باشی: <a href="#in-a-hurry">عجله داری؟</a>. |
Loading…
Reference in new issue