کاربر فرم ثبت‌نام رو کامل کرده، اطلاعاتش ذخیره شده و فقط یه مرحله باقی مونده: ارسال ایمیل خوش‌آمدگویی. حالا سؤال اینجاست؛ واقعاً لازمه کاربر چند ثانیه به صفحه خیره بشه تا اتصال به سرویس ایمیل انجام بشه و پیام ارسال بشه؟

در خیلی از APIها جواب «نه» هست.

بعضی کارها برای تکمیل پاسخ اصلی ضروری نیستن و میشه اول جواب کاربر رو برگردوند و بعد اون عملیات رو انجام داد. به این مدل، اجرای کار بعد از ارسال پاسخ اصلی گفته میشه که در FastAPI با BackgroundTasks قابل انجامه.

مستندات رسمی FastAPI هم ارسال ایمیل و پردازش‌هایی که لازم نیست کاربر منتظر پایانشون بمونه رو از کاربردهای اصلی این قابلیت معرفی میکنن.

BackgroundTasks در FastAPI چیست؟

وقتی یه Endpoint اجرا میشه، معمولاً برنامه تا پایان همه دستورهای داخل اون صبر میکنه و بعد Response رو برای کاربر میفرسته.

حالا اگه وسط همین مسیر ارسال ایمیل دو ثانیه طول بکشه، کاربر هم تقریباً همون دو ثانیه اضافه منتظر میمونه؛ حتی اگه عملیات اصلی ثبت‌نام چند میلی‌ثانیه قبل تموم شده باشه.

FastAPI اجازه میده بعضی تابع‌ها رو به عنوان کار قابل اجرا بعد از ارسال پاسخ ثبت کنیم. اسم این قابلیت BackgroundTasks هست.

یعنی مسیر کلی میتونه اینطوری باشه:

ثبت اطلاعات کاربر ← آماده کردن ارسال ایمیل ← برگردوندن پاسخ ← اجرای ارسال ایمیل

FastAPI کار ثبت‌شده رو بعد از ارسال Response اجرا میکنه.

چرا ارسال ایمیل داخل Endpoint می‌تواند API را کند کند؟

ارسال ایمیل فقط ساختن چند خط متن نیست. برنامه ممکنه مجبور بشه به یه Mail Server یا سرویس خارجی وصل بشه، منتظر پاسخ بمونه و بعد نتیجه ارسال رو دریافت کنه.

همین موضوع درباره بعضی کارهای دیگه هم صدق میکنه؛ مثلاً نوشتن یه Log، ارسال Notification یا انجام یه پردازش سبک بعد از ثبت اطلاعات.

اگر نتیجه این عملیات‌ها برای ساخت Response ضروری نباشه، منتظر نگه داشتن کاربر فقط زمان پاسخ API رو بیشتر میکنه.

اینجاست که جدا کردن کار اصلی از کار جانبی میتونه تجربه کاربر رو بهتر کنه.

استفاده از BackgroundTasks در FastAPI

یه نمونه ساده برای ثبت کاربر و ارسال ایمیل بعد از پاسخ:

from fastapi import BackgroundTasks, FastAPI
import time

app = FastAPI()


def send_welcome_email(email: str):
    # شبیه‌سازی ارسال ایمیل
    time.sleep(2)
    print(f"Welcome email sent to {email}")


@app.post("/register")
async def register_user(
    email: str,
    background_tasks: BackgroundTasks
):
    # اینجا می‌توان اطلاعات کاربر را ذخیره کرد

    background_tasks.add_task(
        send_welcome_email,
        email
    )

    return {
        "message": "User registered successfully"
    }

در این مثال با add_task() به FastAPI میگیم تابع ارسال ایمیل رو بعداً اجرا کن و ایمیل کاربر رو هم به عنوان ورودی بهش بده.

طبق مستندات FastAPI، تابعی که به عنوان کار پس‌زمینه معرفی میشه میتونه هم تابع معمولی Python باشه و هم تابع غیرهمزمان؛ FastAPI نحوه اجرای مناسب اون رو مدیریت میکنه.

کاربر دقیقاً چه چیزی احساس می‌کند؟

بدون اجرای پس‌زمینه، مسیر ممکنه اینطوری باشه:

ثبت کاربر → ارسال ایمیل → انتظار دو ثانیه → Response

با BackgroundTasks:

ثبت کاربر → ثبت Task → Response → ارسال ایمیل

پس از دید کاربر، Endpoint میتونه زودتر پاسخ بده؛ چون لازم نیست برای تموم شدن کار جانبی منتظر بمونه.

البته این به معنی سریع‌تر شدن خود عملیات ارسال ایمیل نیست. ایمیل همچنان ممکنه دو ثانیه طول بکشه؛ فقط کاربر لازم نیست این زمان رو قبل از دریافت Response تحمل کنه.

این تفاوت مهمیه: BackgroundTasks زمان اجرای کار رو جادویی کم نمیکنه، بلکه اون کار رو از مسیر انتظار کاربر خارج میکنه.

چه کارهایی برای BackgroundTasks مناسب هستند؟

کارهای سبک و کوتاهی که نتیجه‌شون برای پاسخ فعلی ضروری نیست، معمولاً گزینه‌های خوبی هستن.

مثلاً ارسال ایمیل اطلاع‌رسانی، ثبت بعضی Logها، ارسال Notification یا انجام یه پردازش کوچک بعد از ذخیره اطلاعات.

FastAPI در مستندات خودش هم ارسال Email Notification رو نمونه مناسب این قابلیت معرفی کرده.

اما یه نکته مهم وجود داره: هر کاری که «پس‌زمینه» است الزاماً نباید با BackgroundTasks انجام بشه.

BackgroundTasks جای Celery را نمی‌گیرد

وقتی یه کار طولانی، سنگین یا مستقل از خود برنامه داریم، بهتره سراغ ابزار قدرتمندتری بریم.

به سیستمی که کارها رو داخل یه صف قرار میده و Workerهای جدا اون‌ها رو پردازش میکنن، صف پردازش کارها گفته میشه. یکی از ابزارهای معروف این حوزه Celery هست.

خود مستندات FastAPI پیشنهاد میکنن برای محاسبات سنگین پس‌زمینه، مخصوصاً زمانی که لازم نیست کار داخل همون Process برنامه اجرا بشه، ابزارهایی مثل Celery مناسب‌تر هستن. این سیستم‌ها میتونن با صف‌هایی مثل Redis یا RabbitMQ کارها رو بین Processها و حتی چند Server توزیع کنن.

پس تفاوت ساده اینه:

BackgroundTasks : کارهای سبک و نزدیک به Request فعلی

Celery :Jobهای سنگین‌تر، مستقل‌تر و قابل توزیع

پردازش ویدیو را به BackgroundTasks نسپارید

یکی از اشتباه‌های رایج اینه که بعد از آشنایی با این قابلیت، هر عملیات سنگینی رو داخلش قرار بدیم.

مثلاً تبدیل یه ویدیوی ۲ گیگابایتی، تحلیل سنگین داده یا ساخت گزارش چنددقیقه‌ای.

این کارها همچنان منابع همون برنامه رو مصرف میکنن و فقط به خاطر اینکه بعد از Response اجرا میشن، سبک نمیشن.

برای چنین Jobهایی بهتره پردازش رو به Worker جدا و سیستم Queue منتقل کنید تا API اصلی درگیر یه عملیات طولانی نشه.

تفاوت async با BackgroundTasks چیست؟

این دو مفهوم زیاد با هم اشتباه گرفته میشن.

وقتی برنامه هنگام انتظار برای شبکه، فایل یا سرویس خارجی میتونه به کارهای دیگه برسه، درباره اجرای غیرمسدودکننده صحبت میکنیم که در Python معمولاً با async و await میبینیم.

اما BackgroundTasks درباره زمان اجرای کار نسبت به Response صحبت میکنه؛ یعنی میگیم این Task لازم نیست قبل از ارسال جواب کاربر تموم بشه.

پس یه تابع میتونه async باشه ولی همچنان قبل از Response اجرا بشه، و یه Task هم میتونه بعد از Response با BackgroundTasks اجرا بشه.

این‌ها دو مسئله متفاوتن.

BackgroundTasks برای کارهای حیاتی کافی است؟

برای عملیات‌های ساده خیلی کاربردیه، ولی برای کارهایی که از دست رفتن اون‌ها قابل قبول نیست باید معماری دقیق‌تری داشته باشید.

مثلاً ارسال یه Notification ساده با ثبت یه تراکنش مالی یکسان نیست.

اگه یه Job باید حتماً اجرا بشه، نیاز به Retry داشته باشه یا باید وضعیت اجرای اون قابل پیگیری باشه، معمولاً Queue و Worker مستقل انتخاب مطمئن‌تریه.

پس انتخاب ابزار باید بر اساس اهمیت و سنگینی کار انجام بشه، نه فقط راحتی نوشتن چند خط کد.

مزیت اصلی BackgroundTasks چیست؟

سادگی.برای اضافه کردن یه کار سبک لازم نیست Redis نصب کنید، Worker جدا بالا بیارید و یه سیستم Queue کامل پیکربندی کنید. FastAPI خودش شیء BackgroundTasks رو در اختیار Endpoint قرار میده و با add_task() میشه تابع موردنظر رو ثبت کرد.

برای پروژه‌های کوچک یا کارهای جانبی ساده، همین قابلیت میتونه کاملاً کافی باشه.

اما هرجا پردازش بزرگ شد، این سادگی نباید باعث بشه محدودیت‌های معماری رو نادیده بگیریم.

جمع‌بندی

BackgroundTasks در FastAPI برای کارهایی ساخته شده که باید بعد از یه Request انجام بشن، ولی کاربر لازم نیست قبل از دریافت Response منتظر تموم شدن اون‌ها بمونه.

ارسال ایمیل، Notification و بعضی عملیات سبک نمونه‌های خوبی هستن. با این روش API میتونه پاسخ اصلی رو سریع‌تر تحویل بده و کار جانبی بعد از اون انجام بشه.

اما برای پردازش‌های سنگین، طولانی یا Jobهایی که نیاز به Queue، Retry و Worker مستقل دارن، بهتره سراغ ابزارهایی مثل Celery برید.

قاعده ساده‌ای که میشه تو ذهن نگه داشت اینه: اگه کار کوتاهه و فقط لازم نیست کاربر منتظرش بمونه، BackgroundTasks رو بررسی کن؛ اگه خودش تبدیل به یه پردازش جدی شده، وقت Queue و Worker رسیده.