اگر از کتابخانههای third party استفاده میکنید که نیاز دارند با استفاده از `await` فراخوانی شوند، مثلاً:
اگه از کتابخونههای third-party استفاده میکنی که بهت میگن باید با `await` صداشون کنی، مثل:
```Python
results = await some_library()
```
آنگاه توابع عملیات مسیر خود را با `async def` تعریف کنید، مانند:
اون وقت، *توابع عملیات مسیرت* رو با `async def` تعریف کن، مثل:
```Python hl_lines="2"
@app.get('/')
@ -22,12 +22,14 @@ async def read_results():
```
/// note
توجه داشته باشید که تنها در توابعی که با `async def` ساخته شدهاند میتوانید از `await` استفاده کنید.
فقط توی توابعی که با `async def` ساخته شدن میتونی از `await` استفاده کنی.
///
---
اگر از کتابخانهای استفاده میکنید که با یک منبع (مانند دیتابیس، API، فایل سیستم و ...) ارتباط برقرار میکند و از استفاده از `await` پشتیبانی نمیکند (که در حال حاضر برای اکثر کتابخانههای دیتابیس رایج است)، آنگاه توابع عملیات مسیر خود را بهصورت عادی و با استفاده از `def` تعریف کنید، مانند:
اگه از یه کتابخونه third-party استفاده میکنی که با یه چیزی (مثل دیتابیس، API، فایل سیستم و غیره) ارتباط داره و از `await` پشتیبانی نمیکنه (که الان بیشتر کتابخونههای دیتابیس اینجورین)، اون وقت *توابع عملیات مسیرت* رو مثل همیشه با `def` معمولی تعریف کن، مثل:
```Python hl_lines="2"
@app.get('/')
@ -38,293 +40,307 @@ def results():
---
اگر برنامهی شما (به هر دلیلی) نیازی به ارتباط با منابع خارجی و انتظار برای پاسخ ندارد، از `async def` استفاده کنید.
اگه برنامهات (به هر دلیلی) نیازی به ارتباط با چیز دیگهای نداره و منتظر جوابش نمیمونه، از `async def` استفاده کن.
---
و در صورتی که مطمئن نیستید، از تعریف معمولی `def` بهره ببرید.
اگه نمیدونی چیکار کنی، از `def` معمولی استفاده کن.
---
**Note**: شما میتوانید به اندازه دلخواه در توابع عملیات مسیر از ترکیب `def` و `async def` استفاده کنید و هر کدام را به بهترین شکل ممکن تعریف کنید. FastAPI بهطور هوشمند با این ترکیبها رفتار میکند.
**توجه**: میتونی به هر تعداد که لازم داری، `def` و `async def` رو توی *توابع عملیات مسیرت* قاطی کنی و هر کدوم رو با بهترین گزینه برای خودت تعریف کنی. FastAPI خودش کار درست رو باهاشون انجام میده.
به هر حال، در هر یک از موارد بالا، FastAPI همچنان بهصورت asynchronous کار کرده و عملکرد بسیار سریعی دارد؛ اما با پیروی از دستورالعملهای فوق، بهینهسازیهای عملکردی بیشتری نیز حاصل میشود.
به هر حال، توی هر کدوم از حالتهای بالا، FastAPI هنوز به صورت ناهمزمان کار میکنه و خیلی سریع عمل میکنه.
ولی با رعایت نکات بالا، میتونه یه سری بهینهسازیهای عملکردی هم انجام بده.
## جزئیات فنی
نسخههای مدرن پایتون از asynchronous code با استفاده از مفهومی به نام **coroutines** و سینتکس **`async` و `await`** پشتیبانی میکنند.
نسخههای مدرن پایتون از **"کد ناهمزمان"** پشتیبانی میکنن که با چیزی به اسم **"coroutines"** کار میکنه و از سینتکس **`async` و `await`** استفاده میکنه.
بگذارید این موضوع را در بخشهای زیر بررسی کنیم:
بیاید این جمله رو تکهتکه بررسی کنیم توی بخشهای زیر:
* **Asynchronous Code**
* **کد ناهمزمان**
* **`async` و `await`**
* **Coroutines**
## Asynchronous کد
## کد ناهمزمان
کد ناهمزمان یعنی زبون برنامهنویسی 💬 یه راهی داره که به کامپیوتر / برنامه 🤖 بگه توی یه جای کد، باید منتظر بمونه تا *یه چیز دیگه* جایی دیگه تموم بشه. فرض کنیم اون *چیز دیگه* اسمش "فایل کند" 📝 باشه.
Asynchronous code به این معناست که زبان برنامهنویسی میتواند به کامپیوتر/برنامه بگوید که در نقطهای از کد، باید منتظر تکمیل یک کار دیگر شود. فرض کنید آن کار "slow-file" نامیده شود.
پس توی اون مدت، کامپیوتر میتونه بره یه کار دیگه انجام بده، تا وقتی که "فایل کند" 📝 تموم بشه.
در این مدت، کامپیوتر میتواند کار دیگری انجام دهد در حالی که "slow-file" مشغول انجام وظیفه است.
بعدش کامپیوتر / برنامه 🤖 هر وقت فرصتی پیدا کرد برمیگرده، چون دوباره منتظره یا وقتی همه کارایی که اون لحظه داشته تموم شدن. اون موقع میچرخه ببینه کدوم از کارایی که منتظرشون بوده تموم شدن و هر کاری که باید باهاشون بکنه رو انجام میده.
سپس، هر زمان که فرصت یافت، کامپیوتر/برنامه برمیگردد؛ یا وقتی که تمام کارهایش به پایان رسیده، بررسی میکند که آیا وظایفی که منتظر آنها بوده، تکمیل شدهاند و سپس کار مربوط به آنها را ادامه میدهد.
بعدش، اولین کاری که تموم شده (مثلاً "فایل کند" 📝) رو برمیداره و هر چیزی که باید باهاش انجام بده رو ادامه میده.
به عنوان مثال، اولین وظیفهای که تکمیل میشود (مثلاً "slow-file") انتخاب شده و ادامهی پردازش روی آن انجام میشود.
این "منتظر چیزی دیگه بودن" معمولاً به عملیات <abbrtitle="ورودی و خروجی">I/O</abbr> اشاره داره که نسبتاً "کند" هستن (در مقایسه با سرعت پردازنده و حافظه RAM)، مثل منتظر موندن برای:
این "انتظار برای چیز دیگری" معمولاً به عملیاتهای **I/O** اشاره دارد که نسبت به سرعت پردازنده و حافظهی RAM نسبتاً کند هستند؛ مانند انتظار برای:
* دادههایی که از کلاینت از طریق شبکه فرستاده میشن
* دادههایی که برنامهات فرستاده تا از طریق شبکه به کلاینت برسه
* محتوای یه فایل توی دیسک که سیستم باید بخوندش و به برنامهات بده
* محتوایی که برنامهات به سیستم داده تا توی دیسک بنویسه
* یه عملیات API از راه دور
* یه عملیات دیتابیس که تموم بشه
* یه کوئری دیتابیس که نتیجهش برگرده
* و غیره
* ارسال داده از سمت client به شبکه
* دریافت دادهای که برنامه شما ارسال کرده از طریق شبکه به client
* خواندن محتوای یک فایل از دیسک توسط سیستم و ارائه آن به برنامه شما
* نوشتن دادههایی که برنامه شما به سیستم داده است بر روی دیسک
* انجام یک عملیات API از راه دور
* تکمیل یک عملیات دیتابیس
* دریافت نتایج یک query دیتابیس
* و غیره.
چون بیشتر زمان اجرا صرف منتظر موندن برای عملیات <abbrtitle="ورودی و خروجی">I/O</abbr> میشه، به اینا میگن عملیات "I/O bound".
از آنجا که بیشتر زمان اجرا صرف انتظار برای عملیاتهای **I/O** میشود، این عملیاتها "I/O bound" نامیده میشوند.
بهش میگن "ناهمزمان" چون کامپیوتر / برنامه مجبور نیست با کار کند "همزمان" باشه، یعنی دقیقاً لحظهای که کار تموم میشه منتظرش بمونه و هیچ کاری نکنه تا نتیجه رو بگیره و ادامه بده.
این سیستم asynchronous نامیده میشود چون برنامه مجبور نیست دقیقا synchronized با کار کند باشد و منتظر لحظهی دقیق پایان آن بماند تا نتیجه را بگیرد؛ بلکه پس از اتمام کار، میتواند برای چند میکروثانیه در صف بماند تا زمانی که کار دیگری به پایان برسد و سپس دوباره به کار خود ادامه دهد.
به جاش، چون یه سیستم "ناهمزمانه"، وقتی کار تموم شد، میتونه یه کم توی صف منتظر بمونه (چند میکروثانیه) تا کامپیوتر / برنامه هر کاری که رفته انجام بده رو تموم کنه، بعد برگرده نتیجه رو بگیره و باهاش کار کنه.
برای کارهای synchronous (در مقابل asynchronous) معمولاً از واژهی "sequential" استفاده میشود، زیرا در این حالت، برنامه تمامی مراحل را به ترتیب انجام داده و سپس به وظیفهی بعدی میپردازد، حتی اگر این مراحل شامل زمان انتظار نیز شوند.
برای "همزمان" (برخلاف "ناهمزمان") معمولاً از اصطلاح "ترتیبی" هم استفاده میکنن، چون کامپیوتر / برنامه همه مراحل رو به ترتیب دنبال میکنه قبل از اینکه بره سراغ یه کار دیگه، حتی اگه اون مراحل شامل منتظر موندن باشن.
### همزمانی و برگر ( Concurrency and Burgers)
### همزمانی و برگرها
این مفهوم asynchronous code که توضیح داده شد، گاهی به عنوان **concurrency** نیز شناخته میشود. لازم به ذکر است که concurrency با parallelism متفاوت است.
ایده **ناهمزمان** که بالا توضیح دادم گاهی بهش **"همزمانی"** هم میگن. این با **"موازیسازی"** فرق داره.
هر دو مفهوم به "اتفاق افتادن چند کار بهطور همزمان" اشاره دارند، اما جزئیات بین آنها بسیار متفاوت است.
**همزمانی** و **موازیسازی** هر دو به "اتفاق افتادن چیزای مختلف تقریباً همزمان" ربط دارن.
برای درک تفاوت، داستان زیر دربارهی burgers را تصور کنید:
ولی جزئیات بین *همزمانی* و *موازیسازی* خیلی متفاوته.
### همبرگرهای همزمان (Concurrent Burgers)
برای دیدن فرقش، این داستان برگرها رو تصور کن:
فرض کنید با crush خود به فستفود میروید. در صف ایستادهاید در حالی که cashier سفارشهای مشتریان جلوی شما را میگیرد. 😍
### برگرهای همزمان
با عشقت میری فستفود بخری، توی صف وایمیستی تا صندوقدار سفارش آدمای جلوییت رو بگیره. 😍
تصاویر قشنگ از <ahref="https://www.instagram.com/ketrinadrawsalot"class="external-link"target="_blank">کترینا تامپسون</a>. 🎨
/// info
تصاویر زیبا با افتخار توسط <ahref="https://www.instagram.com/ketrinadrawsalot"class="external-link"target="_blank">Ketrina Thompson</a> ارائه شده است. 🎨
///
---
تصور کنید شما در این داستان به عنوان یک برنامه هستید.
تصور کن تو توی این داستان کامپیوتر / برنامه 🤖 هستی.
در حالی که در صف هستید، کاملاً idle بوده و منتظر نوبت خود میمانید، بدون اینکه کار بسیار "productive" انجام دهید. اما صف سریع است چون cashier تنها سفارشها را میگیرد (نه آمادهسازی آنها)؛ بنابراین مشکلی نیست.
تا وقتی توی صفی، فقط بیکاری 😴 و منتظر نوبتت هستی، کار خیلی "مفیدی" نمیکنی. ولی صف سریع پیش میره چون صندوقدار فقط سفارش میگیره (آماده نمیکنه)، پس اشکالی نداره.
سپس وقتی نوبت شما میشود، کارهای واقعی و "productive" انجام میدهید؛ منو را بررسی میکنید، تصمیم میگیرید چه میخواهید، انتخاب crush را میگیرید، پرداخت میکنید، چک میکنید که قبض یا کارت درست ارائه شده، اطمینان حاصل میکنید که مبلغ به درستی شارژ شده و سفارش شامل موارد صحیح است و غیره.
بعد که نوبتت میشه، کار "مفید" واقعی میکنی، منو رو نگاه میکنی، تصمیم میگیری چی میخوای، انتخاب عشقت رو میپرسی، پول میدی، چک میکنی اسکناس یا کارت درست باشه، مطمئن میشی درست حساب شده، سفارش رو چک میکنی که درست باشه و غیره.
اما سپس، حتی اگر هنوز burgers خود را دریافت نکرده باشید، کار شما با cashier "on pause" میشود، زیرا باید منتظر باشید تا burgers آماده شوند.
ولی بعدش، حتی اگه هنوز برگراتو نگرفتی، کارت با صندوقدار "متوقف" ⏸ میشه، چون باید منتظر بمونی 🕙 تا برگرات آماده بشن.
اما زمانی که از پیشخوان دور میشوید و به میز مینشینید (با شماره نوبت در دست)، میتوانید توجه خود را به crush تغییر دهید و روی آن "work" کنید. سپس دوباره کاری "productive" مانند flirting با crush انجام میدهید.
ولی وقتی از پیشخون دور میشی و با شماره نوبتت سر میز میشینی، میتونی توجهت رو به عشقت عوض کنی 🔀 و رو اون "کار" ⏯ 🤓 کنی. اون موقع دوباره یه کار خیلی "مفید" داری که معاشقه با عشقته 😍.
بعد، cashier اعلام میکند که "Burgers آماده شدند" و شمارهی شما را روی نمایشگر قرار میدهد؛ اما شما بلافاصله واکنش نشان نمیدهید، زیرا میدانید هیچکس burgers شما را نخواهد گرفت چون شماره نوبت شما مشخص است.
بعد صندوقدار 💁 با گذاشتن شمارهات رو تابلوی پیشخون میگه "من با درست کردن برگرا تموم کردم"، ولی تو فوراً مثل دیوونهها وقتی شمارهات میاد نمیپری. میدونی کسی برگراتو نمیدزده چون شماره نوبتت رو داری و اونا هم مال خودشون رو دارن.
پس منتظر میمانید تا crush داستان خود را تمام کند (یا کار فعلی به پایان برسد)، به آرامی لبخند میزنید و اعلام میکنید که میروید burgers را دریافت کنید.
پس منتظر میمونی تا عشقت داستانش رو تموم کنه (کار فعلی ⏯ / تسک در حال پردازش 🤓 تموم بشه)، با لبخند آروم میگی که میری برگرا رو بیاری ⏸.
سپس به پیشخوان باز میگردید، کار اولیه که اکنون به پایان رسیده را تکمیل میکنید، burgers را دریافت میکنید، تشکر میکنید و آنها را به میز میبرید. این کار آن مرحله از تعامل با پیشخوان را به پایان میرساند و در عوض، وظیفه جدید "خوردن burgers" ایجاد میکند؛ در حالی که کار قبلی "دریافت burgers" به پایان رسیده است.
بعد میری پیشخون 🔀، به تسک اولیه که حالا تموم شده ⏯ برمیگردی، برگرا رو میگیری، تشکر میکنی و میبرشون سر میز. این مرحله / تسک تعامل با پیشخون رو تموم میکنه ⏹. این خودش یه تسک جدید درست میکنه، یعنی "خوردن برگرا" 🔀 ⏯، ولی تسک قبلی "گرفتن برگرا" تموم شده ⏹.
### برگرهای موازی (Parallel Burgers)
### برگرهای موازی
حال تصور کنید اینها "Concurrent Burgers" نیستند بلکه "Parallel Burgers" هستند.
فرض کنید با crush خود برای دریافت fast food به یک محل میروید.
با عشقت میری فستفود موازی بخری.
در صف ایستادهاید، در حالی که چندین cashier (مثلاً ۸ نفر) که همزمان به عنوان cook عمل میکنند، سفارشهای مشتریان جلوی شما را میگیرند.
توی صف وایمیستی در حالی که چند تا (مثلاً 8 تا) صندوقدار که همزمان آشپز هم هستن سفارش آدمای جلوییت رو میگیرن.
همهی مشتریان جلوی شما منتظر میمانند تا burgers آنها آماده شود و قبل از ترک پیشخوان، هر کدام cashier مربوطه به سرعت سفارش را آماده میکند و سپس سفارش بعدی را میگیرد.
همه قبل از تو منتظرن تا برگراشون آماده بشه و از پیشخون برن چون هر کدوم از 8 تا صندوقدار میره و برگر رو همون موقع درست میکنه قبل از اینکه سفارش بعدی رو بگیره.
در حالی که شما و crush مشغول جلوگیری از عبور دیگران و دزدیدن burgers هستید، نمیتوانید توجه خود را به crush معطوف کنید. 😞
چون تو و عشقت مشغول اینی که نذاری کسی جلوتون دربیاد و برگراتو بگیره، نمیتونی به عشقت توجه کنی. 😞
این حالت، کار synchronous است؛ شما کاملاً با cashier/cook هماهنگ هستید 👨🍳. باید منتظر بمانید و در لحظهی دقیق اتمام burgers و تحویل آنها حضور داشته باشید، در غیر این صورت ممکن است کسی دیگری آنها را بگیرد.
این کار "همزمانه"، تو با صندوقدار/آشپز 👨🍳 "همزمان" هستی. باید منتظر بمونی 🕙 و دقیقاً همون لحظه که صندوقدار/آشپز 👨🍳 برگرا رو تموم میکنه و بهت میده اونجا باشی، وگرنه ممکنه یکی دیگه برشون داره.
در این حالت، صحبت یا flirting چندانی وجود ندارد چرا که بیشتر زمان صرف انتظار جلوی پیشخوان شده است. 😞
چون بیشتر وقتتون صرف انتظار 🕙 جلوی پیشخون شده، حرف زدن یا معاشقه زیادی نبوده. 😞
/// اطلاعات
تصاویر قشنگ از <ahref="https://www.instagram.com/ketrinadrawsalot"class="external-link"target="_blank">کترینا تامپسون</a>. 🎨
/// info
تصاویر زیبا با افتخار توسط <ahref="https://www.instagram.com/ketrinadrawsalot"class="external-link"target="_blank">Ketrina Thompson</a> ارائه شده است. 🎨
///
---
در این سناریو برای Parallel Burgers، شما به عنوان یک برنامه با دو processor (شما و crush) هستید که هر دو برای مدت طولانی منتظر میمانند تا "روی پیشخوان" حضور داشته باشند.
توی این سناریوی برگرهای موازی، تو یه کامپیوتر / برنامه 🤖 با دو تا پردازنده (تو و عشقت) هستی که هردوتون مدت طولانی منتظر 🕙 و متمرکز ⏯ روی "انتظار جلوی پیشخون" 🕙 بودید.
فستفود مذکور دارای ۸ processor (cashier/cook) است، در حالی که فروشگاه Concurrent Burgers ممکن است تنها دارای ۲ processor (یک cashier و یک cook) باشد.
فستفود 8 تا پردازنده (صندوقدار/آشپز) داره. در حالی که فستفود برگرهای همزمان ممکنه فقط 2 تا (یه صندوقدار و یه آشپز) داشته باشه.
اما باز هم تجربه نهایی مطلوب نیست. 😞
ولی با این حال، تجربه نهایی خیلی خوب نیست. 😞
---
این داستان معادل Parallel برای burgers است. 🍔
این یه داستان معادل موازی برای برگرها بود. 🍔
برای یه مثال "واقعیتر" از زندگی، یه بانک رو تصور کن.
برای یک مثال "real life" از این موضوع، تصور کنید که در یک بانک هستید.
تا همین چند وقت پیش، بیشتر بانکها چند تا صندوقدار 👨💼👨💼👨💼👨💼 داشتن و یه صف بزرگ 🕙🕙🕙🕙🕙🕙🕙🕙.
تا چندی پیش، اکثر بانکها دارای چندین cashier 👨💼👨💼👨💼👨💼 و صفهای طولانی بودند 🕙🕙🕙🕙🕙🕙🕙🕙.
همه صندوقدارا با یه مشتری بعد از اون یکی کار میکردن 👨💼⏯.
تمام cashierها یکی پس از دیگری با یک مشتری کار میکردند 👨💼⏯.
و تو باید توی صف کلی منتظر میموندی 🕙 وگرنه نوبتت رو از دست میدادی.
و شما مجبور بودید برای مدت طولانی در صف بمانید تا نوبتتان برسد، در غیر این صورت نوبتتان از دست میرفت.
احتمالاً نمیخوای عشقت 😍 رو با خودت ببری بانک 🏦 برای کارای روزمره.
احتمالاً نمیخواستید crush خود را همراه داشته باشید تا به بانک بروید. 🏦😍
### نتیجهگیری برگرها
### نتیجه گیری برگر (Burger Conclusion)
توی این سناریوی "فستفود برگر با عشقت"، چون کلی انتظار 🕙 داری، خیلی منطقیتره که یه سیستم همزمان ⏸🔀⏯ داشته باشی.
در این سناریو از "fast food burgers with your crush"، به دلیل وجود زمانهای طولانی انتظار، استفاده از یک سیستم concurrent بسیار منطقیتر به نظر میرسد.
این برای بیشتر برنامههای وب هم صدق میکنه.
این حالت برای اکثر برنامههای وب صادق است.
کاربرای خیلی خیلی زیاد، ولی سرورت منتظره 🕙 تا اتصال نهچندان خوبشون درخواستاشون رو بفرسته.
کاربران زیادی وجود دارند، اما سرور شما منتظر connectionهای ضعیف آنها برای ارسال requestها میماند.
و بعد دوباره منتظره 🕙 تا جوابها برگردن.
سپس دوباره منتظر پاسخها میماند.
این "انتظار" 🕙 توی میکروثانیهها اندازهگیری میشه، ولی در کل، جمعش میشه کلی انتظار.
این "waiting" در میکروثانیه اندازهگیری میشود، اما در مجموع، زمان انتظار بسیار زیاد است.
برای همین استفاده از کد ناهمزمان ⏸🔀⏯ برای APIهای وب خیلی منطقیه.
به همین دلیل است که استفاده از asynchronous code برای web APIs بسیار منطقی به نظر میرسد.
این نوع ناهمزمانی همون چیزیه که NodeJS رو محبوب کرد (هرچند NodeJS موازی نیست) و نقطه قوت زبان برنامهنویسی Go هم همینه.
همین نوع asynchronous است که باعث محبوبیت NodeJS شد (حتی اگر NodeJS parallel نباشد) و همینطور نقطه قوت زبان برنامهنویسی Go محسوب میشود.
و این همون سطح عملکردیه که با **FastAPI** به دست میاری.
همچنین با داشتن همزمان parallelism و asynchronicity، عملکرد بالاتری نسبت به اکثر چارچوبهای آزمایششدهی NodeJS به دست میآورید که با Go (یک زبان compiled نزدیک به C<ahref="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1"class="external-link"target="_blank">(همه به لطف Starlette)</a>) قابل رقابت است.
و چون میتونی همزمانی و موازیسازی رو همزمان داشته باشی، عملکرد بهتری نسبت به بیشتر فریمورکهای تستشده NodeJS و همتراز با Go که یه زبان کامپایلشده نزدیک به C هست میگیرید<ahref="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1"class="external-link"target="_blank">(همه اینا به لطف Starlette)</a>.
### آیا concurrency بهتر از parallelism است؟
### آیا همزمانی از موازیسازی بهتره؟
خیر! این پیام اصلی داستان نیست.
نه! این نتیجه داستان نیست.
Concurrency با parallelism متفاوت است و در سناریوهای **خاص** که شامل زمان انتظار زیاد هستند، عملکرد بهتری دارد. به همین دلیل، برای توسعهی برنامههای وب بهطور کلی concurrency بهتر از parallelism است؛ اما نه برای همه موارد.
همزمانی با موازیسازی فرق داره. و توی **سناریوهای خاص** که کلی انتظار دارن بهتره. به همین دلیل، معمولاً برای توسعه برنامههای وب خیلی بهتر از موازیسازیه. ولی نه برای همه چیز.
برای توضیح بیشتر، داستان کوتاه زیر را تصور کنید:
برای اینکه تعادل رو حفظ کنیم، این داستان کوتاه رو تصور کن:
> شما باید یک خانهی بزرگ و کثیف را تمیز کنید.
> باید یه خونه بزرگ و کثیف رو تمیز کنی.
*بله، این تمام داستان است.*
*همینه، کل داستان همینه*.
---
در این حالت جایی برای waiting وجود ندارد، فقط مقدار زیادی کار در نقاط مختلف خانه باید انجام شود.
هیچ انتظاری 🕙 نیست، فقط کلی کار که باید توی جاهای مختلف خونه انجام بشه.
ممکن است بتوانید همانند مثال burgers، ابتدا اتاق نشیمن و سپس آشپزخانه را تمیز کنید؛ اما از آنجا که در این کار نیازی به waiting نیست و فقط تمیزکاری میکنید، نوبتبندی تأثیری نخواهد داشت.
میتونی مثل مثال برگرها نوبت بذاری، اول پذیرایی، بعد آشپزخونه، ولی چون منتظر چیزی نیستی 🕙 و فقط داری تمیز میکنی، نوبتها چیزی رو عوض نمیکنن.
مدت زمان لازم برای اتمام کار با یا بدون turns (concurrency) تقریباً یکسان خواهد بود و مقدار کاری که انجام میدهید نیز تغییر نخواهد کرد.
با نوبت یا بدون نوبت (همزمانی) همون مقدار زمان میبره تا تموم کنی و همون مقدار کار رو انجام دادی.
اما در این حالت، اگر بتوانید ۸ نفر (مانند cashier/cook سابق که حالا cleaner هستند) همراه داشته باشید و هر کدام (به همراه شما) یک ناحیه از خانه را به عهده بگیرند، میتوانید تمام کار را بهصورت parallel انجام دهید و با کمک اضافی، خیلی زودتر کار تمام شود.
ولی توی این موقعیت، اگه بتونی اون 8 تا صندوقدار/آشپز/حالا-تمیزکار رو بیاری و هر کدوم (به علاوه خودت) یه بخش خونه رو تمیز کنه، میتونی همه کار رو **موازی** انجام بدی، با کمک اضافه، و خیلی زودتر تموم کنی.
در این سناریو، هر یک از cleaners (از جمله شما) مانند یک processor عمل میکنند و هر کدام قسمت مشخصی از کار را انجام میدهند.
توی این سناریو، هر کدوم از تمیزکارا (از جمله تو) یه پردازندهان که دارن بخش خودشون از کار رو انجام میدن.
و از آنجا که بیشتر زمان اجرا صرف کار واقعی (به جای waiting) میشود و کار در کامپیوتر توسط <abbrtitle="Central Processing Unit">CPU</abbr> انجام میشود، این مسائل "CPU bound" نامیده میشوند.
و چون بیشتر زمان اجرا صرف کار واقعی میشه (نه انتظار)، و کار توی کامپیوتر توسط <abbrtitle="واحد پردازش مرکزی">CPU</abbr> انجام میشه، به این مشکلات میگن "CPU bound".
---
مثالهای رایج برای عملیاتهای CPU bound، مسائلی هستند که نیاز به پردازش ریاضی پیچیده دارند.
مثالهای رایج عملیات CPU bound چیزایی هستن که نیاز به پردازش ریاضی پیچیده دارن.
برای مثال:
مثلاً:
* **Audio** یا **image processing**.
* **Computer vision**: یک تصویر از میلیونها پیکسل تشکیل شده است؛ هر پیکسل شامل ۳ value/رنگ است و پردازش آن معمولاً نیاز به انجام محاسباتی بر روی این پیکسلها بهطور همزمان دارد.
* **Machine Learning**: معمولاً به ضربهای فراوان matrix و vector نیاز دارد. تصور کنید یک spreadsheet عظیم با اعداد که همهی آنها بهطور همزمان در حال ضرب شدن هستند.
* **Deep Learning**: که زیرشاخهای از Machine Learning است؛ در اینجا نیز همان موضوع صدق میکند. تفاوت اینجاست که تنها یک spreadsheet برای ضرب وجود ندارد بلکه مجموعهی عظیمی از اعداد وجود دارد و در بسیاری از موارد از پردازندههای ویژهای برای ساخت و/یا استفاده از این مدلها بهره گرفته میشود.
* پردازش **صدا** یا **تصویر**.
* **بینایی کامپیوتری**: یه تصویر از میلیونها پیکسل تشکیل شده، هر پیکسل 3 تا مقدار / رنگ داره، پردازشش معمولاً نیاز داره روی این پیکسلا همزمان چیزی محاسبه بشه.
* **یادگیری ماشین**: معمولاً کلی ضرب "ماتریس" و "بردار" میخواد. یه صفحه گسترده بزرگ با اعداد رو تصور کن که همهشون رو همزمان ضرب میکنی.
* **یادگیری عمیق**: یه زیرشاخه از یادگیری ماشینه، پس همون چیزا صدق میکنه. فقط اینجا یه صفحه گسترده نیست، بلکه یه مجموعه بزرگ از اونا داری و توی خیلی موارد از یه پردازنده خاص برای ساختن یا استفاده از این مدلها استفاده میکنی.
### Concurrency + Parallelism: Web + Machine Learning
### همزمانی + موازیسازی: وب + یادگیری ماشین
با **FastAPI**شما میتوانید از concurrency بهره ببرید که در توسعه وب بسیار رایج است (همان ویژگی جذاب NodeJS).
با **FastAPI**میتونی از همزمانی که توی توسعه وب خیلی رایجه (همون جذابیت اصلی NodeJS) استفاده کنی.
اما همچنین میتوانید از مزایای parallelism و multiprocessing (اجرای چند فرآیند بهطور موازی) برای کارهای **CPU bound** مانند سیستمهای Machine Learning بهره ببرید.
ولی میتونی از مزایای موازیسازی و multiprocessing (اجرای چند پروسه به صورت موازی) هم برای کارای **CPU bound** مثل سیستمهای یادگیری ماشین بهره ببری.
این موضوع به علاوهی این واقعیت ساده است که Python زبان اصلی Data Science، Machine Learning و بهویژه Deep Learning محسوب میشود، FastAPI را گزینهی بسیار مناسبی برای APIها و اپلیکیشنهای وب مربوط به Data Science/Machine Learning میسازد (در میان بسیاری دیگر).
این، به علاوه این واقعیت ساده که پایتون زبان اصلی برای **علوم داده**، یادگیری ماشین و بهخصوص یادگیری عمیقه، باعث میشه FastAPI یه گزینه خیلی خوب برای APIها و برنامههای وب علوم داده / یادگیری ماشین باشه (بین خیلی چیزای دیگه).
برای مشاهده نحوهی دستیابی به این parallelism در محیط production، به بخش [Deployment](deployment/index.md){.internal-link target="_blank"} مراجعه کنید.
برای دیدن اینکه چطور توی محیط واقعی این موازیسازی رو پیاده کنی، بخش [استقرار](deployment/index.md){.internal-link target=_blank} رو ببین.
## `async` و `await`
نسخههای مدرن Python روشی بسیار شهودی برای تعریف asynchronous code ارائه میدهند. این موضوع باعث میشود کد کاملاً شبیه به کد sequential به نظر برسد و عملیات waiting در لحظات مناسب به طور خودکار انجام شود.
نسخههای مدرن پایتون یه راه خیلی ساده و قابلفهم برای تعریف کد ناهمزمان دارن. این باعث میشه مثل کد "ترتیبی" معمولی به نظر بیاد و توی لحظههای درست برات "منتظر بمونه".
زمانی که عملیاتی وجود دارد که قبل از ارائهی نتیجه نیاز به waiting دارد و از این ویژگیهای جدید پشتیبانی میکند، میتوانید آن را به این صورت بنویسید:
وقتی یه عملیاتی هست که باید قبل از دادن نتیجه منتظر بمونه و از این قابلیتهای جدید پایتون پشتیبانی میکنه، میتونی اینجوری کدنویسی کنی:
```Python
burgers = await get_burgers(2)
```
کلید اینجا استفاده از `await` است. این کلمه به Python میگوید که باید ⏸ منتظر بماند تا `get_burgers(2)` کار خود را به اتمام برساند و سپس نتیجه را در متغیر `burgers` ذخیره کند. بدین ترتیب، Python میداند که در همین حین میتواند کار دیگری انجام دهد (مثلاً دریافت request دیگری).
note کلیدی اینجا `await` هست. به پایتون میگه باید منتظر بمونه ⏸ تا `get_burgers(2)` کارش 🕙 تموم بشه قبل از اینکه نتیجه رو توی `burgers` ذخیره کنه. با این، پایتون میفهمه که میتونه بره یه کار دیگه 🔀 ⏯ انجام بده توی این مدت (مثلاً یه درخواست دیگه رو بگیره).
برای اینکه `await` کار کند، لازم است که در داخل تابعی استفاده شود که از asynchronous بودن پشتیبانی میکند. برای این منظور، کافیست تابع را با `async def` تعریف کنید:
برای اینکه `await` کار کنه، باید توی یه تابع باشه که از این ناهمزمانی پشتیبانی کنه. برای این کار، فقط با `async def` تعریفش کن:
```Python hl_lines="1"
async def get_burgers(number: int):
# انجام کارهای asynchronous برای آمادهسازی burgers
# یه سری کار ناهمزمان برای ساختن برگرا انجام بده
return burgers
```
...به جای:
...به جای`def`:
```Python hl_lines="2"
# این کد asynchronous نیست
# این ناهمزمان نیست
def get_sequential_burgers(number: int):
# انجام کارهای sequential برای آمادهسازی burgers
# یه سری کار ترتیبی برای ساختن برگرا انجام بده
return burgers
```
با استفاده از `async def`، Python میداند که در داخل آن تابع باید از عبارات `await` آگاه باشد و میتواند اجرای آن تابع را pause داده و قبل از بازگشت، کار دیگری انجام دهد.
با `async def`، پایتون میفهمه که توی اون تابع باید حواسش به عبارتهای `await` باشه و میتونه اجرای اون تابع رو "متوقف" کنه ⏸ و بره یه کار دیگه بکنه 🔀 قبل از اینکه برگرده.
هنگامی که میخواهید از تابعی که با `async def` تعریف شده فراخوانی کنید، باید آن را "await" کنید. به عنوان مثال، کد زیر کار نخواهد کرد:
وقتی میخوای یه تابع `async def` رو صدا کنی، باید "منتظرش" بمونی. پس این کار نمیکنه:
```Python
# این کد کار نمیکند، چون get_burgers با async def تعریف شده است
# این کار نمیکنه، چون get_burgers با async def تعریف شده
burgers = get_burgers(2)
```
---
بنابراین، اگر از کتابخانهای استفاده میکنید که به شما میگوید میتوانید آن را با `await` فراخوانی کنید، باید توابع عملیات مسیر مربوطه را با `async def` تعریف کنید، مانند:
پس اگه از یه کتابخونه استفاده میکنی که میگه میتونی با `await` صداش کنی، باید *توابع عملیات مسیرت* که ازش استفاده میکنن رو با `async def` بسازی، مثل:
```Python hl_lines="2-3"
@app.get('/burgers')
@ -333,93 +349,96 @@ async def read_burgers():
return burgers
```
### جزئیات فنی بیشتر
### جزئیات فنیتر
شاید متوجه شده باشید که `await` تنها در داخل توابع تعریفشده با `async def` قابل استفاده است.
شاید متوجه شده باشی که `await` فقط توی توابعی که با `async def` تعریف شدن میتونه استفاده بشه.
اما در عین حال، توابع تعریفشده با `async def` نیز باید "await" شوند؛ یعنی این توابع تنها در داخل توابع دیگری که با `async def` تعریف شدهاند قابل فراخوانی هستند.
ولی در عین حال، توابعی که با `async def` تعریف شدن باید "منتظر" بمونن. پس توابع با `async def` فقط توی توابع دیگهای که با `async def` تعریف شدن میتونن صدا بشن.
پس سوال پیش میآید: دربارهی مسئلهی "egg and chicken"، چگونه اولین تابع `async` را فراخوانی کنیم؟
حالا، مرغ و تخممرغ کدوم اول بود؟ چطور اولین تابع `async` رو صدا میکنی؟
اگر با **FastAPI** کار میکنید، نیازی به نگرانی ندارید؛ چرا که اولین تابع شما همان path operation function خواهد بود و FastAPI میداند چگونه کار را انجام دهد.
اگه با **FastAPI** کار میکنی، لازم نیست نگران این باشی، چون اون "اولین" تابع همون *تابع عملیات مسیرت* هست و FastAPI میدونه چطور کار درست رو بکنه.
اما اگر میخواهید بدون FastAPI از `async`/`await` استفاده کنید، همچنان میتوانید این کار را انجام دهید.
ولی اگه بخوای بدون FastAPI از `async`/`await` استفاده کنی، اونم میتونی.
### نوشتن کد asynchronous خودتان
### کد ناهمزمان خودت رو بنویس
Starlette (و **FastAPI**) بر پایهی<ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a>ساخته شدهاند که باعث میشود با هر دو کتابخانهی استاندارد Python یعنی<ahref="https://docs.python.org/3/library/asyncio-task.html"class="external-link"target="_blank">asyncio</a> و <ahref="https://trio.readthedocs.io/en/stable/"class="external-link"target="_blank">Trio</a> سازگار باشد.
Starlette (و **FastAPI**) بر پایه <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a>هستن، که باعث میشه با هر دو کتابخونه استاندارد پایتون<ahref="https://docs.python.org/3/library/asyncio-task.html"class="external-link"target="_blank">asyncio</a> و <ahref="https://trio.readthedocs.io/en/stable/"class="external-link"target="_blank">Trio</a> سازگار باشه.
بهویژه، شما میتوانید مستقیماً از <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a> برای سناریوهای پیشرفته concurrency استفاده کنید که نیاز به الگوهای پیچیدهتر در کد شما دارد.
بهخصوص، میتونی مستقیماً از <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a> برای موارد پیشرفتهتر همزمانی که نیاز به الگوهای پیچیدهتر توی کد خودت داری استفاده کنی.
و حتی اگر از FastAPI استفاده نمیکنید، میتوانید اپلیکیشنهای asynchronous خود را با <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a> بنویسید تا سازگاری بالا و مزایای آن (مانند structured concurrency) را دریافت کنید.
حتی اگه از FastAPI هم استفاده نمیکردی، میتونی برنامههای ناهمزمان خودت رو با <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a> بنویسی که خیلی سازگار باشه و مزایاش (مثل *structured concurrency*) رو بگیری.
من همچنین یک کتابخانهی دیگر به عنوان یک لایهی نازک بر روی AnyIO ایجاد کردهام تا type annotations را بهبود بخشم و **autocompletion** و **inline errors** بهتری ارائه دهم. این کتابخانه همچنین دارای معرفی و آموزش دوستانهای برای کمک به شما در درک و نوشتن کد asynchronous خودتان است: <ahref="https://asyncer.tiangolo.com/"class="external-link"target="_blank">Asyncer</a>. این کتابخانه بهویژه برای زمانی مفید است که نیاز به ترکیب کد asynchronous با کد عادی (blocking/synchronous) داشته باشید.
من یه کتابخونه دیگه رو بالای AnyIO ساختم، بهعنوان یه لایه نازک، که تایپنوتیشنها رو بهتر کنه و **اتوکامپلیشن**، **خطاهای درونخطی** و غیره رو بهتر کنه. یه مقدمه و آموزش ساده هم داره که بهت کمک میکنه **بفهمی** و **کد ناهمزمان خودت رو بنویسی**: <ahref="https://asyncer.tiangolo.com/"class="external-link"target="_blank">Asyncer</a>. اگه بخوای **کد ناهمزمان رو با کد معمولی** (بلاککننده/همزمان) ترکیب کنی خیلی بهدردت میخوره.
### اشکال دیگر کد asynchronous
### شکلهای دیگه کد ناهمزمان
این سبک استفاده از `async` و `await`نسبتاً جدید در Python است.
این سبک استفاده از `async` و `await`توی زبان نسبتاً جدیده.
اما باعث میشود کار با asynchronous code بسیار سادهتر شود.
ولی کار با کد ناهمزمان رو خیلی سادهتر میکنه.
این سینتکس (یا تقریباً مشابه آن) اخیراً در نسخههای مدرن JavaScript (در Browser و NodeJS) نیز گنجانده شده است.
همین سینتکس (یا تقریباً مشابه) تازگیها توی نسخههای مدرن جاوااسکریپت (توی مرورگر و NodeJS) هم اضافه شده.
اما پیش از آن، مدیریت asynchronous code بسیار پیچیدهتر و دشوارتر بود.
ولی قبل از اون، مدیریت کد ناهمزمان خیلی پیچیدهتر و سختتر بود.
در نسخههای قبلی Python، میتوانستید از threads یا <ahref="https://www.gevent.org/"class="external-link"target="_blank">Gevent</a> استفاده کنید؛ اما کد بسیار پیچیدهتری برای درک، رفع اشکال و تفکر نیاز داشت.
توی نسخههای قدیمیتر پایتون، میتونستی از threadها یا <ahref="https://www.gevent.org/"class="external-link"target="_blank">Gevent</a> استفاده کنی. ولی کدش خیلی سختتر فهمیده میشه، دیباگ میشه و بهش فکر میشه.
در نسخههای قبلی NodeJS/Browser JavaScript، از "callbacks" استفاده میکردند که به اصطلاح منجر به "callback hell" میشد.
توی نسخههای قدیمیتر NodeJS / جاوااسکریپت مرورگر، از "callbackها" استفاده میکردی. که منجر به <ahref="http://callbackhell.com/"class="external-link"target="_blank">callback hell</a> میشد.
## Coroutines
**Coroutine** اصطلاحی بسیار شیک برای شیئی است که توسط تابعی که با `async def` تعریف شده بازگردانده میشود. Python میداند که این شیء شبیه یک تابع است که میتواند شروع شود و در نهایت به پایان برسد، اما ممکن است بهطور داخلی نیز در لحظاتی که `await` وجود دارد، pause کند.
**Coroutine** یه اصطلاح خیلی شیکه برای چیزی که یه تابع `async def` برمیگردونه. پایتون میفهمه که این یه چیزی مثل تابع هست، که میتونه شروع بشه و یه جایی تموم بشه، ولی ممکنه توی خودش هم "متوقف" ⏸ بشه، هر وقت که یه `await` توش باشه.
تمام این قابلیتهای استفاده از asynchronous code با `async` و `await` اغلب به صورت "coroutines" خلاصه میشود. این مفهوم قابل مقایسه با ویژگی اصلی در Go، یعنی "Goroutines" است.
ولی همه این قابلیت استفاده از کد ناهمزمان با `async` و `await` خیلی وقتها خلاصه میشه به استفاده از "coroutines". این قابلمقایسهست با قابلیت اصلی Go، یعنی "Goroutines".
## Conclusion
## نتیجهگیری
بیایید همان جمله بالا را مرور کنیم:
بیاید همون جمله از بالا رو ببینیم:
> نسخههای مدرن Python از asynchronous code با استفاده از مفهومی به نام coroutines و سینتکس `async` و `await` پشتیبانی میکنند.
> نسخههای مدرن پایتون از **"کد ناهمزمان"** با چیزی به اسم **"coroutines"** پشتیبانی میکنن، با سینتکس **`async` و `await`**.
امیدواریم اکنون برای شما معنای این موضوع واضحتر شده باشد. ✨
حالا باید بیشتر براتون جا بیفته. ✨
همین است که FastAPI (از طریق Starlette) را به آنچه که عملکرد بسیار چشمگیری دارد، مجهز میکند.
همه اینا چیزیه که به FastAPI (از طریق Starlette) قدرت میده و باعث میشه این عملکرد شگفتانگیز رو داشته باشه.
## جزئیات بسیار فنی
## جزئیات خیلی فنی
/// warning
شاید بتوانید این بخش را رد بزنید.
اینها جزئیات بسیار فنی دربارهی نحوهی عملکرد درونی FastAPI هستند.
اگر دانش فنی کافی (مانند coroutines، threads، blocking و غیره) دارید و کنجکاو هستید که بدانید FastAPI چگونه با `async def` در مقابل `def` رفتار میکند، ادامه دهید.
احتمالاً میتونی از این بگذری.
اینا جزئیات خیلی فنی از نحوه کار **FastAPI** زیر پوستهست.
اگه دانش فنی خوبی داری (coroutineها، threadها، بلاک کردن و غیره) و کنجکاوی که FastAPI چطور `async def` رو در مقابل `def` معمولی مدیریت میکنه، ادامه بده.
///
### Path operation functions
### توابع عملیات مسیر
وقتی یک path operation function را با استفاده از `def` معمولی به جای `async def` تعریف میکنید، آن تابع در یک threadpool خارجی اجرا میشود که سپس await میشود، به جای اینکه بهصورت مستقیم فراخوانی شود (چون این کار میتواند باعث blocking سرور شود).
وقتی یه *تابع عملیات مسیر* رو با`def` معمولی به جای `async def` تعریف میکنی، توی یه threadpool خارجی اجرا میشه که بعدش منتظرش میمونن، به جای اینکه مستقیم صدا بشه (چون اگه مستقیم باشه سرور رو بلاک میکنه).
اگر از چارچوب asynchronous دیگری آمدهاید که به شیوه توضیح دادهشده کار نمیکند و عادت دارید توابع عملیات مسیر محض محاسباتی را با `def` تعریف کنید (برای به دست آوردن افزایش جزئی عملکرد به اندازهای حدود ۱۰۰ نانوثانیه)، توجه داشته باشید که در FastAPI اثر دقیقا برعکس خواهد بود. در این موارد بهتر است از `async def` استفاده کنید مگر اینکه توابع عملیات مسیر شما از کدی استفاده کنند که عملیات blocking (I/O) انجام میدهد.
اگه از یه فریمورک ناهمزمان دیگه اومدی که اینجوری کار نمیکنه و عادت داری توابع ساده فقط محاسباتی رو با `def` معمولی برای یه افزایش عملکرد کوچیک (حدود 100 نانوثانیه) تعریف کنی، توجه کن که توی **FastAPI** اثرش برعکسه. توی این موارد، بهتره از `async def` استفاده کنی مگه اینکه *توابع عملیات مسیرت* کدی داشته باشن که عملیات بلاککننده <abbrtitle="ورودی/خروجی: خوندن یا نوشتن دیسک، ارتباطات شبکه">I/O</abbr> انجام بده.
با این حال، در هر دو حالت احتمالاً FastAPI همچنان [سریعتر خواهد بود](index.md#performance){.internal-link target="_blank"} (یا حداقل قابل مقایسه با) چارچوب قبلی شما.
با این حال، توی هر دو حالت، احتمالش زیاده که **FastAPI** [هنوز سریعتر باشه](index.md#performance){.internal-link target=_blank} از (یا حداقل قابلمقایسه باشه با) فریمورک قبلیات.
### وابستگی ها (Dependencies)
### وابستگیها
همین موضوع برای [dependencies](tutorial/dependencies/index.md){.internal-link target="_blank"} نیز صادق است. اگر یک dependency بهصورت تابع `def` استاندارد تعریف شده باشد به جای `async def`، در threadpool خارجی اجرا میشود.
همین برای [وابستگیها](tutorial/dependencies/index.md){.internal-link target=_blank} هم صدق میکنه. اگه یه وابستگی تابع `def` معمولی باشه به جای `async def`، توی threadpool خارجی اجرا میشه.
### وابستگی های فرعی (Sub-dependencies)
### زیروابستگیها
شما میتوانید چندین dependency و [sub-dependency](tutorial/dependencies/sub-dependencies.md){.internal-link target="_blank"} داشته باشید که به یکدیگر وابستهاند (به عنوان پارامترهای تعاریف توابع). برخی از آنها ممکن است با `async def` و برخی با `def` ساخته شوند. باز هم کار میکند و آنهایی که با `def` ساخته شدهاند در یک threadpool خارجی فراخوانی میشوند به جای اینکه await شوند.
میتونی چند تا وابستگی و [زیروابستگی](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} داشته باشی که همدیگه رو نیاز دارن (بهعنوان پارامترهای تعریف تابع)، بعضیا ممکنه با `async def` ساخته بشن و بعضیا با `def` معمولی. بازم کار میکنه، و اونایی که با `def` معمولی ساخته شدن توی یه thread خارجی (از threadpool) صدا میشن به جای اینکه "منتظر" بمونن.
### سایر utility functions
### توابع کمکی دیگه
هر تابع utility دیگری که مستقیماً فراخوانی میکنید، میتواند با استفاده از `def` معمولی یا `async def` تعریف شود و FastAPI تاثیری در نحوهی فراخوانی آن ندارد.
هر تابع کمکی دیگهای که مستقیم خودت صداش میکنی میتونه با `def` معمولی یا `async def` ساخته بشه و FastAPI روی نحوه صداکردنش تأثیر نمیذاره.
این موضوع در تضاد با توابعی است که FastAPI برای شما فراخوانی میکند: path operation functions و dependencies.
این برخلاف توابعی هست که FastAPI برات صدا میکنه: *توابع عملیات مسیر* و وابستگیها.
اگر تابع utility شما بهصورت تابعی معمولی (`def`) تعریف شده باشد، مستقیماً (همانطور که در کد نوشتهاید) فراخوانی میشود و در threadpool اجرا نمیشود؛ اما اگر تابع با `async def` ساخته شده باشد، باید هنگام فراخوانی آن از `await` استفاده کنید.
اگه تابع کمکیت یه تابع معمولی با `def` باشه، مستقیم (همونجوری که توی کدت نوشتی) صدا میشه، نه توی threadpool، اگه تابع با `async def` ساخته شده باشه، باید وقتی توی کدت صداش میکنی براش `await` بذاری.
---
باز هم، اینها جزئیات بسیار فنی هستند که احتمالاً مفید خواهند بود اگر به دنبال آنها باشید.
باز هم، اینا جزئیات خیلی فنیان که اگه دنبالشون اومده باشی احتمالاً برات مفیدن.
در غیر این صورت، کافی است از دستورالعملهای بخش In a hurry? پیروی کنید.
وگرنه، با راهنماییهای بخش بالا: <ahref="#in-a-hurry">عجله داری؟</a> باید اوکی باشی.