Async/Await: ماهیت، توابع async و کار با await

نویسنده: IT Sectr منتشر شده: 2026-03-16 زمان مطالعه: 8 دقیقه

Async/Await — جفت کلمه کلیدی برای نوشتن کد نامتقارن به سبک همزمان است، در Swift از iOS 13 و در Kotlin با کوروتین‌ها قابل دسترسی است. به گزارش Apple Swift Documentation, 2026، Async/Await جایگزین زنجیره‌های callback و GCD شده و کد نامتقارن را خطی و خوانا می‌کند. کلمه کلیدی async تابع را به عنوان نامتقارن علامت گذاری می‌کند، و await اجرای آن را تا دریافت نتیجه متوقف می‌کند.

نکات کلیدی

  • Async/Await — شکر نحوی برای کد نامتقارن بدون callback‌های تودرتو
  • async تابع را به عنوان نامتقارن و قابل توقف علامت گذاری می‌کند
  • await اجرای تابع را تا پایان عملیات نامتقارن متوقف می‌کند
  • Task در Swift یک واحد کاری نامتقارن جدید با زمینه ایجاد می‌کند
  • Structured Concurrency تضمین می‌دهد که تمام وظایف فرعی پیش از وظیفه والد تکمیل شوند

Async/Await و برنامه‌نویسی نامتقارن چیست

Async/Await — یک ساختار زبانی است که به شما اجازه می‌دهد کد نامتقارن را به یک شکل خطی مانند کد همزمان بنویسید. در Swift این قابلیت در iOS 13 / macOS 10.15 همراه با چارچوب Swift Concurrency ظاهر شد. در Kotlin، async/await از طریق کتابخانه کوروتین (kotlinx.coroutines) از نسخه 1.3 قابل دسترس است.

پیش از ظهور async/await، کد نامتقارن بر پایه callback‌ها، GCD یا RxSwift ساخته می‌شد. زنجیره‌های callback تودرتو به callback hell — تودرتوی عمیق که خواندن و اشکال‌زدایی را دشوار می‌کند منتهی می‌شد. Async/Await این مشکل را حل می‌کند و به توابع نامتقارن اجازه می‌دهد در نقاط await متوقف شوند و پس از دریافت نتیجه ادامه دهند.

به گزارش گوگل (2025)، استفاده از async/await در Kotlin تعداد خطوط کد نامتقارن را 40–60% در مقایسه با رویکرد callback کاهش می‌دهد. در Swift، پذیرش Swift Concurrency در سال 2025 به 68% در میان برنامه‌های منتشرشده رسید. iOS 16 پیشتیبانی از Swift Concurrency را در UIKit، SwiftUI و Foundation اضافه کرد که async/await را به ابزاری استاندارد برای تمام پروژه‌های جدید Apple تبدیل کرد.

اصل کار: توابع قابل توقف

تفاوت کلیدی async/await از تردانه‌ها — توقف بدون بلوک کردن. وقتی تابع به await برسد، متوقف می‌شود و تردانه فعلی را برای سایر وظایف آزاد می‌کند. پس از تکمیل عملیات نامتقارن، تابع در همان تردانه یا تردانه‌ای دیگر ادامه می‌یابد. به این مولتی‌تاسکینگ همکارانه گفته می‌شود.

چگونه Async/Await کار می‌کند: توقف و ازسرنوگیری

تابع async به یک ماشین حالت — یک اتومات متناهی که حالت‌های اجرا را مدیریت می‌کند کمپیل می‌شود. در نقطه await، کمپایلر زمینه (متغیرهای محلی، آدرس بازگشت) را ذخیره و کنترل را به runtime انتقال می‌دهد. وقتی نتیجه آماده شد، runtime زمینه را بازیابی و اجرا را ادامه می‌دهد.

در Swift، این ماشین حالت در سطح کمپایلر SIL (Swift Intermediate Language) پیاده‌سازی شده است. در Kotlin — از طریق مکانیسم Continuation Passing Style (CPS): هر تابع suspend یک پارامتر مخفی Continuation دریافت می‌کند که نتیجه از طریق آن برگردانده می‌شود. هر دو رویکرد بازیابی ایمن را بدون نشت حافظه تضمین می‌کنند.

  • Swift — کمپایلر تابع async را با runAsync coroutine frame به SIL تبدیل می‌کند
  • Kotlin — هر تابع suspend یک Continuation دریافت و اجرا را به dispatcher دلگات می‌کند
  • هر دو زبان — تردانه را در حین await آزاد می‌کنند و آن را در زمان انتظار بلوک نمی‌کنند
  • استثناها — با مکانیسم‌های استاندارد (throws / try-catch) منتشر می‌شوند

Async/Await در مقابل Callback‌ها و GCD: مقایسه رویکردها

رویکرد callback نیازمند ارسال یک بسته است که پس از تکمیل عملیات فراخوانده می‌شود. در سه درخواست پیاپی، سه سطح تودرتو با مدیریت خطا در هر سطح ایجاد می‌شود. Async/Await همان سه درخواست را به سه خط پیاپی با یک بلوک واحد catch تبدیل می‌کند.

GCD (DispatchQueue) مشکل callback hell را از طریق صف‌های serial و DispatchGroup حل می‌کند، اما پرگویی باقی می‌ماند. برای یک وظیفه ساده — بارگیری user، سپس friends، سپس UI — نیازمند ایجاد صف‌ها، گروه‌ها و بلوک‌های notify است. Async/Await همان وظیفه را با سه خط در یک تابع انجام می‌دهد.

جنبهCallbackGCDAsync/Await
تودرتوعمیق (هرم مرگ)متوسط (گروه‌ها+notify)خطی (تخت)
مدیریت خطادر هر callbackدستییکپارچه (try/catch)
لغودستیمحدودTask.cancel()
Thread safetyدستیاز طریق serial queueاز طریق MainActor

به گزارش Apple WWDC 2024، Swift Concurrency async/await رویکرد توصیه‌شده برای کد جدید است. GCD برای ادغام با کتابخانه‌های C و سناریوهای خاص پردازش سطح پایین تردانه باقی مانده است.

Swift Concurrency: Task، TaskGroup و Actor

Task — واحد اصلی کار نامتقارن در Swift است. Task در زمینه یک actor موجود یا روی هر تردانه ای ایجاد می‌شود. داخل Task می‌توان توابع async را از طریق await فراخواند. TaskGroup به شما اجازه می‌دهد چندین وظیفه فرعی را به صورت موازی اجرا کرده و نتایج آنها را جمع‌آوری کنید — معادل DispatchGroup تقویت‌شده.

Actor — یک نوع مرجعی ایمن در جهت تردانه است که وضعیت خود را در برابر شرایط تسابق داده‌ها محافظت می‌کند. کمپایلر تضمین می‌دهد که دسترسی به ویژگی‌های actor فقط از طریق فراخوانی‌های async یا داخل انزوای actor ممکن است. Actor جایگزین serial DispatchQueue برای محافظت از وضعیت مشترک تغییرپذیر بدون قفل‌های دستی است.

Structured Concurrency

اصل Structured Concurrency می‌گوید: هر وظیفه نامتقارن یک والد دارد و والد تا تکمیل تمام فرزندانش تکمیل نمی‌شود. اگر وظیفه A Task { await B() } را اجرا کند، A منتظر B می‌ماند. این از نشت وظایف جلوگیری و چرخه زندگی قابل پیش‌بینی را تضمین می‌کند.

Async/Await در Kotlin با کوروتین‌ها

در Kotlin، async/await از طریق coroutines — تردانه‌های سبک زبان پیاده‌سازی شده است. تابع با کلمه کلیدی suspend علامت‌گذاری می‌شود (معادل async در Swift). برای اجرا از launch (بینیاز-و-فراموش) یا async (با نتیجه) استفاده می‌شود. Await برای دریافت نتیجه از async فراخوانده می‌شود.

Dispatcher تعیین می‌کند که کوروتین در چه استخوان تردانه‌ای اجرا شود: Dispatchers.Main برای UI، Dispatchers.IO برای شبکه/دیسک و Dispatchers.Default برای وظایف پردازش شدید می‌باشد. Structured Concurrency از طریق CoroutineScope تأمین می‌شود — با لغو scope، تمام کوروتین‌های فرعی لغو می‌شوند.

به گزارش JetBrains (2025)، 97% برنامه‌های Android در Google Play از kotlinx.coroutines استفاده می‌کنند و 82% آنها از async/await برای درخواست‌های شبکه استفاده می‌کنند. Coroutines به استاندارد دو فاکتو برای برنامه‌نویسی نامتقارن در Android تبدیل شده‌اند. Kotlin Multiplatform نیز از کوروتین‌ها پشتیبانی می‌کند و امکان استفاده از کد نامتقارن مشترک را در Android، iOS و طرف سرور فراهم می‌کند.

نمونه کدهای Async/Await در Swift و Kotlin

برای هر زبان دو نمونه در نظر می‌گیریم: بارگیری پیاپی داده‌ها و درخواست‌های موازی با TaskGroup یا async/await.

Swift: فراخوانی‌های پیاپی async

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

swift
func loadProfile() async throws -> Profile {
    let user = try await api.fetchUser()
    let friends = try await api.fetchFriends(for: user.id)
    return Profile(user: user, friends: friends)
}

Swift: درخواست‌های موازی با TaskGroup

TaskGroup وظایف را به صورت موازی اجرا کرده و نتایج را در یک آرایه جمع می‌کند. ترتیب نتایج ممکن است با ترتیب اجرا مطابقت نداشته باشد.

swift
func loadParallel() async throws -> [String] {
    await withThrowingTaskGroup(of: String.self) { group in
        group.addTask { try await api.fetchName() }
        group.addTask { try await api.fetchEmail() }
        group.addTask { try await api.fetchAvatar() }
        var results = [String]()
        for try await value in group { results.append(value) }
        return results
    }
}

Kotlin: فراخوانی پیاپی با async/await

توابع suspend می‌توانند سایر توابع suspend را فراخوانند. معادل Swift: فراخوانی‌های پیاپی.

kotlin
suspend fun loadProfile(): Profile {
    val user = api.fetchUser()
    val friends = api.fetchFriends(user.id)
    return Profile(user, friends)
}

Kotlin: درخواست‌های موازی با async/await

در Kotlin، همزمانی از طریق coroutineScope با async برای هر درخواست بدست می‌آید.

kotlin
suspend fun loadParallel(): List<String> = coroutineScope {
    val name = async { api.fetchName() }
    val email = async { api.fetchEmail() }
    val avatar = async { api.fetchAvatar() }
    listOf(name.await(), email.await(), avatar.await())
}

خطاهای رایج و محدودیت‌های Async/Await

Task { } فراموش‌شده — تابع نامتقارن را نمی‌توان از زمینه همزمان بدون پیچیدن در Task فراخواند. در Swift این با خطای کمپیل همراه است. در Kotlin اجرا از طریق lifecycleScope یا viewModelScope ضروری است. تلاش برای فراخوانی تابع suspend از یک تابع عادی کمپیل نخواهد شد.

استفاده بیش از حد از async بدون نیاز

لزومی ندارد هر تابعی نامتقارن باشد. وظایف پردازش مطلق بدون I/O از async سودی نمی‌برند — بهتر است آنها را در DispatchQueue با .userInitiated QoS اجرا کنید. Async/Await برای عملیات‌های I/O-bound: شبکه، دیسک، انتظار بهینه‌سازی شده است.

بلوک کردن تردانه اصلی از طریق .result

استفاده از .result در تردانه اصلی (انتظار همزمان برای نتیجه تابع async) منجر به بلوک شدن می‌شود. در Swift، Task.synchronousWait توصیه نمی‌شود. در Kotlin، runBlocking روی تردانه اصلی یک انتی‌پاترن است — از lifecycleScope استفاده کنید.

نشت Task در حین لغو

اگر Task در ویژگی کلاس ذخیره نشود، پس از خروج از زمینه لغو می‌شود. در Swift، Task محدود به زمینه است — چرخه زندگی آن به زمینه ایجادکننده وابسته است. در صورت نیاز به عملیات طولانی، Task را در یک ویژگی ذخیره کنید.

سوالات متداول

تفاوت async/await با DispatchQueue چیست؟

Async/Await یک ساختار زبانی است که نیازی به ایجاد صف ندارد. DispatchQueue یک API سیستمی برای مدیریت تردانه‌هاست. Async/await در سطح زبان به state machine و GCD در سطح سیستم‌عامل به syscall کمپیل می‌شود.

آیا await تردانه را بلوک می‌کند؟

خیر — await تابع را متوقف می‌کند، اما تردانه را برای سایر وظایف آزاد می‌کند. پس از تکمیل عملیات، تابع در هر تردانه موجودی از استخوان ادامه می‌یابد.

آیا می‌توان وظیفه async را لغو کرد؟

بله — از طریق Task.cancel() در Swift یا Job.cancel() در Kotlin. لغو بر اساس Structured Concurrency به تمام وظایف فرعی سرایت می‌کند. کد خود علم لغو را از طریق Task.isCancelled یا ensureActive() بررسی می‌کند.

MainActor در Swift چیست؟

MainActor یک actor است که ویژگی‌ها و روش‌هایش همیشه روی تردانه اصلی اجرا می‌شوند. با علامت‌گذاری تابع با @MainActor، تضمین می‌دهید که به‌روزرسانی‌های UI روی تردانه درست انجام می‌شوند، جایگزین DispatchQueue.main.async.

در چه زبان‌هایی async/await وجود دارد؟

Async/Await در Swift (iOS 13+)، Kotlin (از طریق kotlinx.coroutines)، Dart (Flutter)، JavaScript/TypeScript، Python، C#، Rust و Go (از طریق goroutines با شکر نحوی) موجود است.

نتیجه‌گیری

  • Async/Await — ساختار زبانی برای کد نامتقارن خطی بدون callback hell
  • Swift Concurrency Task، TaskGroup، Actor و Structured Concurrency را فراهم می‌کند
  • Kotlin coroutines از توابع suspend، Dispatchers و حوزه‌های کوروتین استفاده می‌کند
  • توقف بدون بلوک — تفاوت کلیدی async/await از تردانه‌ها و صف‌ها
  • Structured Concurrency تکمیل تمام وظایف فرعی را قبل از والد تضمین می‌کند
  • Async/Await vs GCD — اولی برای کد جدید و دومی برای legacy و سطح پایین
  • MainActor در Swift و Dispatchers.Main در Kotlin از تردانه UI در برابر عملیات‌های پس‌زمینه محافظت می‌کنند

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید