async: ما هو، Deferred وكيف تعمل الكوروتينات المتوازية

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

async — هو منشئ Coroutine Builder في Kotlin يقوم بتشغيل كوروتين ويعيد كائن Deferred<T> للحصول على نتيجة عملية غير متزامنة. يسمح async بتنفيذ مهام متعددة بالتوازي وتجميع نتائجها عبر await(). وفقًا لـ دليل JetBrains، 2024، يعتبر async الطريقة المفضلة لتنظيم الحسابيات المتوازية في الكوروتينات.

النقاط الرئيسية

  • async — منشئ كوروتين يعيد Deferred<T> بنتيجة عملية غير متزامنة
  • Deferred — وعد غير متزامن، يرث Job، مع طريقة await() للحصول على النتيجة
  • async يسمح بتنفيذ مهام مستقلة بالتوازي، مما يقلل الوقت الإجمالي
  • await() — دالة suspend تعلق الكوروتين حتى الحصول على النتيجة دون حظر
  • التوازي المنظم مع async يضمن إلغاء جميع المهام المتوازية عند حدوث خطأ

ما هو async في Kotlin Coroutines؟

async — هي دالة توسعة لـ CoroutineScope تقوم بإنشاء كوروتين يعيد نتيجة. على عكس launch، تعيد async Deferred<T> — كائنًا يمثل قيمة مستقبلية. يتم تشغيل الكوروتين المُطلَق عبر async بشكل متزامن مع الكوروتينات الأخرى، مما يسمح بتوزية الحسابيات.

يتم استخدام async عندما تكون نتيجة عملية غير متزامنة مطلوبة للمعالجة اللاحقة: تحميل البيانات من مصادر متعددة، معالجة المجموعات بالدفعات، طلبات متوازية لمختلف واجهات البرمجة. يتم إنشاء Deferred فورًا، ويصبح النتيجة متاحة بعد اكتمال الكوروتين.

kotlin
import kotlinx.coroutines.*

suspend fun String.delayedValue(): String {
    delay(1000L)
    return this
}

fun main() = runBlocking {
    val deferred: Deferred<String> = async { "Hello".delayedValue() }
    println(deferred.await())
}

بنية async و Deferred

توقيع async مشابه launch: نفس المعلمات CoroutineContext، CoroutineStart وكتلة suspend. الاختلاف في نوع الإرجاع — Deferred<T> بدلاً من Job، حيث T هو نوع النتيجة التي ترجعها آخر عبارة في الكتلة.

kotlin
public fun <T> CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred<T>

Deferred<T> — واجهة ترث Job وتضيف الطرق await()، getCompleted()، getCompletionExceptionOrNull(). await() هي دالة suspend ويمكن استدعاؤها فقط داخل كوروتين أو دالة suspend أخرى. getCompleted() هي طريقة غير حاجزة تلقي استثناءً إذا لم تكتمل الكوروتين بعد.

المهام المتوازية مع async

المزية الرئيسية لـ async هي القدرة على تشغيل المهام بالتوازي. بدلاً من انتظار كل عملية تسلسليًا (الوقت الإجمالي = مجموع الأوقات)، يقوم async بتشغيل الكوروتينات في نفس الوقت، مما يقلل الوقت الإجمالي إلى مدة أطول عملية.

تحميل البيانات بالتوازي

سيناريو نموذجي — تحميل ملف المستخدم، إعداداته وسجل الطلبات بثلاث طلبات async متوازية. بعد اكتمال الثلاثة، تتم دمج النتائج في نموذج بيانات واحد.

kotlin
suspend fun loadUserDashboard(userId: String): Dashboard = coroutineScope {
    val profile = async { api.getProfile(userId) }
    val settings = async { api.getSettings(userId) }
    val orders = async { api.getRecentOrders(userId) }

    Dashboard(
        profile = profile.await(),
        settings = settings.await(),
        orders = orders.await()
    )
}

تقوم دالة coroutineScope بتشغيل الكوروتينات الفرعية وتنتظر اكتمالها قبل إعادة النتيجة. يتم تنفيذ الكتلة بأكملها بالتوازي، ليس تسلسليًا.

معالجة نتائج Deferred

يدعم Deferred عدة طرق للعمل مع النتيجة. بالإضافة إلى await()، تتوفر طرق للتحقق من الحالة، والحصول على قيمة مكتملة وتجميع عدة Deferred عبر دوال المساعدة لـ Kotlin Coroutines.

الطريقةالوصفSuspend؟
await()تعلق الكوروتين حتى الحصول على النتيجةنعم
getCompleted()تعيد النتيجة إذا اكتملت الكوروتين (إلا ستثناء)لا
getCompletionExceptionOrNull()تعيد الاستثناء عند الخطأ أو nullلا
isCompletedتتحقق ما إذا كانت الكوروتين قد اكتملتلا

لتجميع قائمة من Deferred، يتم استخدام awaitAll() — دالة تعلق التنفيذ حتى اكتمال جميع Deferred في المجموعة. إذا اكتمل واحد على الأقل بخطأ، تلقي awaitAll() الاستثناء.

kotlin
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
    return coroutineScope {
        val deferreds = requests.map { async { it() } }
        deferreds.awaitAll()
    }
}

الاختلافات بين async و launch

يعتمد الاختيار بين async و launch على ما إذا كانت نتيجة الكوروتين مطلوبة. يعيد launch Job وهو مناسب لعمليات fire-and-forget، ويعيد async Deferred ويستخدم للمهام التي تعيد بيانات. من حيث البنية والمعلمات، المنشئات متطابقة.

  • launch → Job (بدون نتيجة)، async → Deferred<T> (نتيجة T)
  • launch — مهام fire-and-forget (التسجيل، التخزين المؤقت، إرسال التحليلات)
  • async — حسابيات متوازية (تحميل من عدة API، معالجة بالدفعات)
  • كلاهما يدعمان التوازي المنظم ومعلمات CoroutineStart
  • await() متاح فقط لـ Deferred، launch لا يوفر نتيجة

قاعدة مهمة: لا تستخدم async لعمليات fire-and-forget. إذا كانت نتيجة الكوروتين غير مطلوبة، استخدم launch. يقوم async بإنشاء تكاليف إضافية لإنشاء Deferred، وهو ما لا يبرر نفسه في مثل هذه السيناريوهات.

أمثلة async في التطوير المحمول

في Android، يتم استخدام async للعمليات المتوازية داخل ViewModel و UseCases. السيناريوهات النموذجية: تحميل الملف الشخصي وخلاصة الأخبار في نفس الوقت، الحصول على الطقس من عدة خدمات جوية، مزامنة البيانات بالدفعات مع الخادم.

kotlin
class HomeViewModel : ViewModel() {
    fun loadHomeScreen() {
        viewModelScope.launch {
            val userData = async(Dispatchers.IO) { repository.getUserData() }
            val newsFeed = async(Dispatchers.IO) { repository.getNewsFeed() }
            val notifications = async(Dispatchers.IO) { repository.getNotifications() }

            val state = HomeState(
                user = userData.await(),
                news = newsFeed.await(),
                unread = notifications.await()
            )
            _uiState.update { state }
        }
    }
}

يوصى بتغليف مجموعة من async في coroutineScope أو supervisorScope لعزل الأخطاء. يسمح supervisorScope لباقي كوروتينات async بالاستمرار حتى إذا فشلت واحدة — مفيد للعمليات غير الحرجة.

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

هل يمكن استخدام async بدون await؟

يمكن، ولكنه بلا جدوى. async بدون await() يقوم بتشغيل كوروتين ولكن نتيجته تفقد. إذا كانت النتيجة غير مطلوبة، استخدم launch — لا يقوم بإنشاء Deferred وهو أكثر كفاءة في الذاكرة والأداء.

كيف يعالج async الاستثناءات؟

يتم تخزين الاستثناء داخل async في Deferred. يقوم استدعاء await() بطرح هذا الاستثناء. للتحقق بدون طرح، استخدم getCompletionExceptionOrNull(). عند استخدام coroutineScope، لغي الخطأ جميع الكوروتينات الفرعية.

ما هو awaitAll في Kotlin Coroutines؟

awaitAll() — دالة توسعة لـ Iterable<Deferred<T>> تعلق الكوروتين حتى اكتمال جميع Deferred. تعيد قائمة من النتائج. إذا فشل واحد على الأقل من Deferred، تلقي الاستثناء وتلغي الباقي.

هل يختلف async عن GlobalScope.async؟

نعم. async هي دالة توسعة لـ CoroutineScope مرتبطة بالنطاق. يقوم GlobalScope.async بإنشاء كوروتين دون ارتباط بدورة الحياة — يمكن أن يعمل بشكل غير محدود. لا يوصى بـ GlobalScope في Android بسبب خطر التسرب.

كم عدد كوروتينات async التي يمكن تشغيلها للطلبات المتوازية؟

يتحدد العدد بواسطة الموزع: يدعم Dispatchers.IO ما يصل إلى 64 مسار متزامنًا، و Dispatchers.Default — حتى عدد نواة وحدة المعالجة. للعمليات الضخمة، استخدم التوازي المحدود عبر Semaphore أو mapNotNull مع awaitAll.

الخلاصة

  • async — منشئ كوروتين يعيد Deferred<T> للحصول على نتائج غير متزامنة
  • Deferred يرث Job ويضيف الطرق await()، getCompleted()، getCompletionExceptionOrNull()
  • التنفيذ المتوازي مع async يقلل الوقت الإجمالي إلى مدة أطول عملية
  • awaitAll() — أداة لانتظار جميع Deferred في مجموعة
  • coroutineScope — غلاف موصى به لمجموعة من async مع ضمان الإلغاء
  • لا تستخدم async لعمليات fire-and-forget — استخدم launch بدلاً
  • في Android، يستخدم async للتحميل المتوازي في ViewModel مع viewModelScope

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

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

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

اقرأ أيضًا