Async/Await هو زوج من الكلمات المفتاحية لكتابة كود غير متزامن بأسلوب متزامن، متاح في Swift منذ iOS 13 وفي Kotlin مع coroutines. وفقًا لـ Apple Swift Documentation, 2026، Async/Await يستبدل سلاسل الاستدعاءات العكسية و GCD، مما يجعل الكود غير المتزامن خطيًا وقابلًا للقراءة. الكلمة المفتاحية async تحدد الدالة على أنها غير متزامنة، و await تعلق تنفيذها حتى يتم استلام النتيجة.
أهم النقاط
Async/Await هو تركيب لغوي يسمح بكتابة كود غير متزامن بنفس الخطية التي يكتب بها الكود المتزامن. في Swift، ظهر في iOS 13 / macOS 10.15 مع إطار عمل Swift Concurrency. في Kotlin، async/await متاح من خلال مكتبة coroutines (kotlinx.coroutines) بدءًا من الإصدار 1.3.
قبل async/await، كان الكود غير المتزامن يُبنى على الاستدعاءات العكسية أو GCD أو RxSwift. سلاسل الاستدعاءات العكسية المتداخلة أدت إلى جحيم الاستدعاءات — تداخل عميق يعقد القراءة والتصحيح. Async/Await يحل هذه المشكلة بالسماح للدوال غير المتزامنة بـ "التعليق" عند نقاط await والاستئناف بعد استلام النتيجة.
وفقًا لـ Google (2025)، استخدام async/await في Kotlin يقلل عدد أسطر الكود غير المتزامن بنسبة 40–60% مقارنة بأسلوب الاستدعاءات العكسية. في Swift، وصل اعتماد Swift Concurrency إلى 68% بين التطبيقات المنشورة في عام 2025. iOS 16 أضاف دعم Swift Concurrency في UIKit و SwiftUI و Foundation، مما جعل async/await الأداة القياسية لجميع مشاريع Apple الجديدة.
الفرق الرئيسي بين async/await والخيوط هو التعليق بدون حظر. عندما تواجه الدالة await، فإنها تعلق، محررةً الخيط الحالي لمهام أخرى. بعد اكتمال العملية غير المتزامنة، تستأنف الدالة على نفس الخيط أو خيط آخر. هذا يسمى تعدد المهام التعاوني.
الدالة غير المتزامنة تُترجم إلى آلة حالات — أتمتة محدودة تدير حالات التنفيذ. عند نقطة await، يحفظ المترجم السياق (المتغيرات المحلية، عنوان العودة) ويسلم التحكم إلى بيئة التشغيل. عندما تكون النتيجة جاهزة، تستعيد بيئة التشغيل السياق وتكمل التنفيذ.
في Swift، هذه الآلة الحالة مُنفذة على مستوى مترجم SIL (Swift Intermediate Language). في Kotlin، يتم استخدام آلية Continuation Passing Style (CPS): كل دالة معلقة تقبل معامل Continuation مخفيًا يتم من خلاله إرجاع النتيجة. كلا الأسلوبين يضمنان استئنافًا آمنًا بدون تسرب للذاكرة.
أسلوب الاستدعاءات العكسية يتطلب تمرير إغلاق يتم استدعاؤه بعد اكتمال العملية. مع ثلاثة طلبات متسلسلة، تحصل على ثلاثة مستويات من التداخل مع معالجة أخطاء في كل مستوى. Async/Await يحول نفس الطلبات الثلاثة إلى ثلاثة أسطر متسلسلة مع كتلة catch واحدة.
GCD (DispatchQueue) يحل مشكلة جحيم الاستدعاءات من خلال قوائم الانتظار التسلسلية و DispatchGroup، لكنه يظل مطولاً. لمهمة بسيطة — تحميل مستخدم، ثم أصدقاء، ثم واجهة — تحتاج إلى قوائم انتظار ومجموعات وكتل إشعار. Async/Await يؤدي نفس المهمة بثلاثة أسطر في دالة واحدة.
| الجانب | الاستدعاء العكسي | GCD | Async/Await |
|---|---|---|---|
| التداخل | عميق (هرم الموت) | متوسط (مجموعات+إشعار) | خطي (مسطح) |
| معالجة الأخطاء | لكل استدعاء | يدوي | موحد (try/catch) |
| الإلغاء | يدوي | محدود | Task.cancel() |
| سلامة الخيوط | يدوي | عبر قائمة تسلسلية | عبر MainActor |
وفقًا لـ Apple WWDC 2024، Swift Concurrency async/await هو الأسلوب الموصى به للكود الجديد. يبقى GCD للتكامل مع مكتبات C وسيناريوهات معالجة الخيوط منخفضة المستوى.
Task هي الوحدة الأساسية للعمل غير المتزامن في Swift. يتم إنشاء Task في سياق ممثل موجود أو على خيط عشوائي. داخل Task، يمكن استدعاء دوال غير متزامنة عبر await. TaskGroup يسمح بتشغيل مهام فرعية متعددة بالتوازي وجمع نتائجها — وهو مماثل لـ DispatchGroup محسّن.
Actor هو نوع مرجعي آمن للخيوط يحمي حالته من سباقات البيانات. يضمن المترجم أن الوصول إلى خصائص الممثل ممكن فقط من خلال استدعاءات غير متزامنة أو ضمن عزلة الممثل. Actor يحل محل DispatchQueue التسلسلية لحماية الحالة القابلة للتغيير المشتركة بدون أقفال يدوية.
مبدأ Structured Concurrency ينص على: كل مهمة غير متزامنة لها أب، والأب لا يكتمل حتى يكتمل جميع أبنائه. إذا قامت المهمة A بتشغيل Task { await B() }، فإن A تنتظر B. هذا يمنع تسرب المهام ويضمن دورة حياة قابلة للتنبؤ.
في Kotlin، async/await مُنفذ من خلال coroutines — خيوط خفيفة الوزن في اللغة. يتم تمييز الدالة بالكلمة المفتاحية suspend (مماثلة لـ async في Swift). للتشغيل، يُستخدم launch (أطلق وانسى) أو async (مع نتيجة). يتم استدعاء Await للحصول على النتيجة من async.
Dispatcher يحدد أي مجموعة خيوط يتم تشغيل coroutine عليها: Dispatchers.Main لواجهة المستخدم، Dispatchers.IO للشبكة/القرص و Dispatchers.Default للمهام كثيفة الاستخدام للمعالج. Structured Concurrency مضمون من خلال CoroutineScope — عند إلغاء النطاق، يتم إلغاء جميع coroutines الفرعية.
وفقًا لـ JetBrains (2025)، 97% من تطبيقات Android في Google Play تستخدم kotlinx.coroutines، و 82% منها تستخدم async/await لطلبات الشبكة. أصبحت coroutines المعيار الفعلي للبرمجة غير المتزامنة في Android. Kotlin Multiplatform يدعم أيضًا coroutines، مما يسمح باستخدام كود غير متزامن مشترك على Android و iOS والخادم.
لنلقِ نظرة على مثالين لكل لغة: تحميل متسلسل للبيانات وطلبات متوازية مع TaskGroup أو async/await.
await يعلق التنفيذ حتى يتم استلام بيانات المستخدم، ثم حتى يتم استلام الأصدقاء. بدون إغلاقات متداخلة.
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)
}
TaskGroup يشغل المهام بالتوازي ويجمع النتائج في مصفوفة. قد لا يتطابق ترتيب النتائج مع ترتيب التشغيل.
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
}
}
الدوال suspend يمكنها استدعاء دوال suspend أخرى. مماثل لـ Swift: استدعاءات متسلسلة.
suspend fun loadProfile(): Profile {
val user = api.fetchUser()
val friends = api.fetchFriends(user.id)
return Profile(user, friends)
}
في Kotlin، يتم تحقيق التوازي من خلال coroutineScope مع async لكل طلب.
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())
}
Task { } المنسي — لا يمكن استدعاء دالة غير متزامنة من سياق متزامن دون تغليفها في Task. في Swift، هذا ينتج خطأ في الترجمة. في Kotlin، يتطلب التشغيل عبر lifecycleScope أو viewModelScope. محاولة استدعاء دالة suspend من دالة عادية لن يتم ترجمتها.
ليست كل دالة تحتاج أن تكون غير متزامنة. المهام كثيفة المعالجة بدون إدخال/إخراج لا تستفيد من async — من الأفضل تنفيذها على DispatchQueue مع QoS .userInitiated. Async/Await محسّن لعمليات الإدخال/الإخراج: الشبكة، القرص، الانتظار.
استخدام .result على الخيط الرئيسي (انتظار متزامن لنتيجة دالة غير متزامنة) يؤدي إلى الحظر. في Swift، Task.synchronousWait غير موصى به من Apple. في Kotlin، runBlocking على الخيط الرئيسي هو نمط سيء — استخدم lifecycleScope.
إذا لم يتم حفظ Task في خاصية الفئة، بعد الخروج من السياق يتم إلغاؤه. في Swift، Task محدود النطاق — دورة حياته مرتبطة بالسياق المنشئ. احفظ Task في خاصية إذا كنت بحاجة إلى عملية طويلة الأمد.
الأسئلة الشائعة
Async/Await هو تركيب لغوي لا يتطلب إنشاء قوائم انتظار. DispatchQueue هي واجهة برمجة تطبيقات نظامية لإدارة الخيوط. Async/await يُترجم إلى آلة حالات على مستوى اللغة، GCD — إلى استدعاءات نظام على مستوى OS.
لا — await يعلق الدالة لكنه يحرر الخيط لمهام أخرى. بعد اكتمال العملية، تستأنف الدالة على أي خيط متاح من المجموعة.
نعم — عبر Task.cancel() في Swift أو Job.cancel() في Kotlin. ينتشر الإلغاء إلى جميع المهام الفرعية وفقًا لـ Structured Concurrency. الكود يتحقق من علامة الإلغاء عبر Task.isCancelled أو ensureActive().
MainActor هو ممثل يتم تنفيذ خصائصه وطرقه دائمًا على الخيط الرئيسي. بتمييز دالة بـ @MainActor، تضمن أن تحديثات واجهة المستخدم تحدث على الخيط الصحيح، لتحل محل DispatchQueue.main.async.
Async/Await متاح في Swift (iOS 13+) و Kotlin (عبر kotlinx.coroutines) و Dart (Flutter) و JavaScript/TypeScript و Python و C# و Rust و Go (عبر goroutines مع سكر نحوي).
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا