Async/Await: الجوهر، الدوال غير المتزامنة والعمل مع await

المؤلف: IT Sectr نُشر: 2026-03-16 وقت القراءة: 8 دق

Async/Await هو زوج من الكلمات المفتاحية لكتابة كود غير متزامن بأسلوب متزامن، متاح في Swift منذ iOS 13 وفي Kotlin مع coroutines. وفقًا لـ Apple Swift Documentation, 2026، Async/Await يستبدل سلاسل الاستدعاءات العكسية و GCD، مما يجعل الكود غير المتزامن خطيًا وقابلًا للقراءة. الكلمة المفتاحية async تحدد الدالة على أنها غير متزامنة، و await تعلق تنفيذها حتى يتم استلام النتيجة.

أهم النقاط

  • Async/Await هو سكر نحوي للكود غير المتزامن بدون استدعاءات عكسية متداخلة
  • async تحدد الدالة على أنها غير متزامنة وقابلة للتعليق
  • await يعلق تنفيذ الدالة حتى اكتمال العملية غير المتزامنة
  • Task في Swift ينشئ وحدة عمل غير متزامنة جديدة مع سياق
  • Structured Concurrency يضمن اكتمال جميع المهام الفرعية قبل المهمة الأم

ما هو 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، فإنها تعلق، محررةً الخيط الحالي لمهام أخرى. بعد اكتمال العملية غير المتزامنة، تستأنف الدالة على نفس الخيط أو خيط آخر. هذا يسمى تعدد المهام التعاوني.

كيف يعمل Async/Await: التعليق والاستئناف

الدالة غير المتزامنة تُترجم إلى آلة حالات — أتمتة محدودة تدير حالات التنفيذ. عند نقطة await، يحفظ المترجم السياق (المتغيرات المحلية، عنوان العودة) ويسلم التحكم إلى بيئة التشغيل. عندما تكون النتيجة جاهزة، تستعيد بيئة التشغيل السياق وتكمل التنفيذ.

في Swift، هذه الآلة الحالة مُنفذة على مستوى مترجم SIL (Swift Intermediate Language). في Kotlin، يتم استخدام آلية Continuation Passing Style (CPS): كل دالة معلقة تقبل معامل Continuation مخفيًا يتم من خلاله إرجاع النتيجة. كلا الأسلوبين يضمنان استئنافًا آمنًا بدون تسرب للذاكرة.

  • Swift — المترجم يترجم الدالة غير المتزامنة إلى SIL مع runAsync coroutine frame
  • Kotlin — كل دالة معلقة تستلم Continuation وتفوض التنفيذ إلى الموزع
  • كلا اللغتين — تحرران الخيط عند await، بدون حجبه أثناء الانتظار
  • الاستثناءات — تنتشر عبر الآليات القياسية (throws / try-catch)

Async/Await مقابل الاستدعاءات العكسية و GCD: مقارنة الأساليب

أسلوب الاستدعاءات العكسية يتطلب تمرير إغلاق يتم استدعاؤه بعد اكتمال العملية. مع ثلاثة طلبات متسلسلة، تحصل على ثلاثة مستويات من التداخل مع معالجة أخطاء في كل مستوى. Async/Await يحول نفس الطلبات الثلاثة إلى ثلاثة أسطر متسلسلة مع كتلة catch واحدة.

GCD (DispatchQueue) يحل مشكلة جحيم الاستدعاءات من خلال قوائم الانتظار التسلسلية و DispatchGroup، لكنه يظل مطولاً. لمهمة بسيطة — تحميل مستخدم، ثم أصدقاء، ثم واجهة — تحتاج إلى قوائم انتظار ومجموعات وكتل إشعار. Async/Await يؤدي نفس المهمة بثلاثة أسطر في دالة واحدة.

الجانبالاستدعاء العكسيGCDAsync/Await
التداخلعميق (هرم الموت)متوسط (مجموعات+إشعار)خطي (مسطح)
معالجة الأخطاءلكل استدعاءيدويموحد (try/catch)
الإلغاءيدويمحدودTask.cancel()
سلامة الخيوطيدويعبر قائمة تسلسليةعبر MainActor

وفقًا لـ Apple WWDC 2024، Swift Concurrency async/await هو الأسلوب الموصى به للكود الجديد. يبقى GCD للتكامل مع مكتبات C وسيناريوهات معالجة الخيوط منخفضة المستوى.

Swift Concurrency: Task و TaskGroup و Actor

Task هي الوحدة الأساسية للعمل غير المتزامن في Swift. يتم إنشاء Task في سياق ممثل موجود أو على خيط عشوائي. داخل Task، يمكن استدعاء دوال غير متزامنة عبر await. TaskGroup يسمح بتشغيل مهام فرعية متعددة بالتوازي وجمع نتائجها — وهو مماثل لـ DispatchGroup محسّن.

Actor هو نوع مرجعي آمن للخيوط يحمي حالته من سباقات البيانات. يضمن المترجم أن الوصول إلى خصائص الممثل ممكن فقط من خلال استدعاءات غير متزامنة أو ضمن عزلة الممثل. Actor يحل محل DispatchQueue التسلسلية لحماية الحالة القابلة للتغيير المشتركة بدون أقفال يدوية.

Structured Concurrency

مبدأ Structured Concurrency ينص على: كل مهمة غير متزامنة لها أب، والأب لا يكتمل حتى يكتمل جميع أبنائه. إذا قامت المهمة A بتشغيل Task { await B() }، فإن A تنتظر B. هذا يمنع تسرب المهام ويضمن دورة حياة قابلة للتنبؤ.

Async/Await في Kotlin مع coroutines

في 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 والخادم.

أمثلة كود مع Async/Await في Swift و Kotlin

لنلقِ نظرة على مثالين لكل لغة: تحميل متسلسل للبيانات وطلبات متوازية مع TaskGroup أو async/await.

Swift: استدعاءات غير متزامنة متسلسلة

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 بدون حاجة

ليست كل دالة تحتاج أن تكون غير متزامنة. المهام كثيفة المعالجة بدون إدخال/إخراج لا تستفيد من async — من الأفضل تنفيذها على DispatchQueue مع QoS .userInitiated. Async/Await محسّن لعمليات الإدخال/الإخراج: الشبكة، القرص، الانتظار.

حظر الخيط الرئيسي عبر .result

استخدام .result على الخيط الرئيسي (انتظار متزامن لنتيجة دالة غير متزامنة) يؤدي إلى الحظر. في Swift، Task.synchronousWait غير موصى به من Apple. في Kotlin، runBlocking على الخيط الرئيسي هو نمط سيء — استخدم lifecycleScope.

تسرب Task عند الإلغاء

إذا لم يتم حفظ Task في خاصية الفئة، بعد الخروج من السياق يتم إلغاؤه. في Swift، Task محدود النطاق — دورة حياته مرتبطة بالسياق المنشئ. احفظ Task في خاصية إذا كنت بحاجة إلى عملية طويلة الأمد.

الأسئلة الشائعة

كيف يختلف async/await عن DispatchQueue؟

Async/Await هو تركيب لغوي لا يتطلب إنشاء قوائم انتظار. DispatchQueue هي واجهة برمجة تطبيقات نظامية لإدارة الخيوط. Async/await يُترجم إلى آلة حالات على مستوى اللغة، GCD — إلى استدعاءات نظام على مستوى OS.

هل await يحظر الخيط؟

لا — await يعلق الدالة لكنه يحرر الخيط لمهام أخرى. بعد اكتمال العملية، تستأنف الدالة على أي خيط متاح من المجموعة.

هل يمكن إلغاء مهمة غير متزامنة؟

نعم — عبر Task.cancel() في Swift أو Job.cancel() في Kotlin. ينتشر الإلغاء إلى جميع المهام الفرعية وفقًا لـ Structured Concurrency. الكود يتحقق من علامة الإلغاء عبر Task.isCancelled أو ensureActive().

ما هو MainActor في Swift؟

MainActor هو ممثل يتم تنفيذ خصائصه وطرقه دائمًا على الخيط الرئيسي. بتمييز دالة بـ @MainActor، تضمن أن تحديثات واجهة المستخدم تحدث على الخيط الصحيح، لتحل محل DispatchQueue.main.async.

في أي اللغات يوجد async/await؟

Async/Await متاح في Swift (iOS 13+) و Kotlin (عبر kotlinx.coroutines) و Dart (Flutter) و JavaScript/TypeScript و Python و C# و Rust و Go (عبر goroutines مع سكر نحوي).

الملخص

  • Async/Await هو تركيب لغوي للكود غير المتزامن الخطي بدون جحيم الاستدعاءات
  • Swift Concurrency يوفر Task و TaskGroup و Actor و Structured Concurrency
  • Coroutines في Kotlin تستخدم دوال suspend و Dispatchers ونطاقات coroutine
  • التعليق بدون حظر هو الفرق الرئيسي بين async/await والخيوط وقوائم الانتظار
  • Structured Concurrency يضمن اكتمال جميع المهام الفرعية قبل المهمة الأم
  • Async/Await vs GCD — الأول مفضل للكود الجديد، والثاني للكود القديم ومنخفض المستوى
  • MainActor في Swift و Dispatchers.Main في Kotlin يحميان خيط واجهة المستخدم من العمليات الخلفية

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا