Coroutine Builder: ما هو، أنواع بناة الكوروتينات وكيفية عملها

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

Coroutine Builder — دوال Kotlin Coroutines التي تنشئ وتشغل الكوروتينات، وتحدد طريقة تنفيذها. تغطي البنّاءات launch و async و runBlocking و produce سيناريوهات مختلفة: من المهام الخلفية إلى الحسابات المتوازية مع إرجاع النتيجة. وفقًا لـ JetBrains, 2024، Coroutine Builder هو أساس نموذج الكوروتينات، حيث يوفر التزامن المنظم وإدارة دورة الحياة.

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

  • Coroutine Builder — دالة تنشئ كوروتين داخل CoroutineScope محدد
  • launch — يشغل كوروتين دون إرجاع نتيجة، ويعيد كائن Job
  • async — يشغل كوروتين مع إرجاع Deferred، مما يسمح بالحصول على النتيجة عبر await()
  • runBlocking — يحجب الخيط الحالي، يُستخدم كجسر بين الكود الحاجب والمعلق
  • produce — ينشئ كوروتين مع قناة لنقل البيانات (مهمل لصالح Flow)

ما هو Coroutine Builder؟

Coroutine Builder هي دالة توسعة في Kotlin تأخذ CoroutineScope وكتلة suspend، وتنشئ وتشغل كوروتين جديد. يحدد كل باني كيفية تنفيذ الكوروتين: مع أو بدون إرجاع نتيجة، مع حجب الخيط أو بشكل غير متزامن. البنّاءات هي نقاط الدخول إلى نموذج الكوروتينات في اللغة.

تعمل جميع البنّاءات عبر CoroutineScope، الذي يدير دورة حياة الكوروتينات الفرعية. عند إلغاء النطاق، يتم إلغاء جميع الكوروتينات المشغلة عبره تلقائيًا — هذا هو مبدأ التزامن المنظم. يمنع هذا النهج تسرب الكوروتينات ويضمن إنهاءً قابلًا للتنبؤ.

kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    // تعمل البنّاءات داخل CoroutineScope
    val job = launch {
        delay(1000L)
        println("العالم!")
    }
    println("مرحبًا،")
    job.join()
}

الأنواع الرئيسية لبناة الكوروتينات

يوفر Kotlin أربعة بناة كوروتينات مدمجة: launch و async و runBlocking و produce. لكل منها نوع إرجاعه الخاص ومجال تطبيقه. لتطوير Android المحمول، البنّاءان الرئيسيان هما launch و async — يعملان بطريقة غير حاجبة ويتكاملان مع المكونات المعمارية.

البانينوع الإرجاعحجب الخيطالسيناريو
launchJobلامهام fire-and-forget
asyncDeferred<T>لاحسابات متوازية
runBlockingTنعماختبارات، دالة main
produceReceiveChannel<E>لانقل بيانات (مهمل)

يقبل كل باني معاملات إضافية: CoroutineStart (استراتيجية التشغيل)، CoroutineContext (الموزع، الاستثناءات) وكتلة كود مسماة. افتراضيًا، يبدأ الكوروتين فورًا (CoroutineStart.DEFAULT).

launch: تنفيذ fire-and-forget

launch هو الباني الأكثر استخدامًا في تطوير Android. يشغل كوروتين لا يُرجع نتيجة، ويعيد كائن Job لإدارة دورة حياته. هذا هو الخيار المثالي للعمليات التي تحتاج فقط إلى تأثير جانبي: الحفظ في قاعدة البيانات، إرسال التحليلات، تحديث واجهة المستخدم.

صياغة واستخدام launch

يقبل باني launch CoroutineScope و CoroutineContext اختياري وكتلة suspend. يسمح Job الذي يُرجع بإلغاء الكوروتين أو انتظار اكتماله أو التحقق من حالته.

kotlin
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
val job: Job = scope.launch(CoroutineStart.LAZY) {
    val data = fetchFromNetwork()
    saveToDatabase(data)
}
job.start()
job.join()

المعامل CoroutineStart.LAZY يؤجل التنفيذ حتى استدعاء صريح لـ start() أو join(). هذا مفيد للتهيئة البطيئة والتنفيذ الشرطي. للتنفيذ الفوري القياسي، يُستخدم CoroutineStart.DEFAULT أو يُحذف المعامل.

async: حسابات متوازية مع نتيجة

async هو باني يُرجع Deferred<T> — وعد غير متزامن بنتيجة. استدعاء await() يعلق الكوروتين حتى الحصول على النتيجة، دون حجب الخيط. هذه هي الآلية الرئيسية للمهام المتوازية في كوروتينات Kotlin.

تشغيل طلبين بالتوازي

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

kotlin
suspend fun fetchUserData(): UserData {
    val deferred1 = CoroutineScope(Dispatchers.IO).async { api.getProfile() }
    val deferred2 = CoroutineScope(Dispatchers.IO).async { api.getSettings() }
    val deferred3 = CoroutineScope(Dispatchers.IO).async { api.getNotifications() }
    return UserData(
        profile = deferred1.await(),
        settings = deferred2.await(),
        notifications = deferred3.await()
    )
}

Deferred يرث من Job، لذلك يدعم async جميع عمليات دورة الحياة: الإلغاء، انتظار الاكتمال، معالجة الاستثناءات. عند إلغاء النطاق، يتم إلغاء كوروتينات Deferred الفرعية تلقائيًا.

runBlocking: جسر إلى العالم الحاجب

runBlocking هو الباني الوحيد الذي يحجب الخيط الحالي حتى اكتمال الكوروتين. ينشئ CoroutineScope جديد ويشغل الكوروتين المُمرر، محجبًا الخيط المستدعي. يُستخدم في نقاط الدخول main()، في الاختبارات، وعند التكامل مع كود حاجب.

متى يكون runBlocking ضروريًا

runBlocking مبرر في ثلاثة سيناريوهات: نقطة دخول التطبيق (main)، اختبارات الوحدات لدوال suspend، والتكامل مع المكتبات القائمة على الاستدعاءات حيث لا يمكن استخدام suspend. في كود Android الإنتاجي، استخدام runBlocking على الخيط الرئيسي غير موصى به بشدة.

kotlin
class CoroutineTest {
    @Test
    fun `test suspend function`() = runBlocking {
        val result = mySuspendFunction()
        assertEquals("expected", result)
    }
}

للاختبارات، يُوصى باستخدام kotlinx-coroutines-test مع TestCoroutineDispatcher بدلاً من runBlocking — وهذا يوفر التحكم في الوقت ويتجنب الحجب في بيئة الاختبار.

كيفية اختيار الباني المناسب

يعتمد اختيار Coroutine Builder على النتيجة المعادة وسيناريو التنفيذ. إذا كانت العملية لا تتطلب إرجاع بيانات — استخدم launch. إذا كنت بحاجة لنتيجة عملية غير متزامنة — استخدم async. استخدم runBlocking فقط للجسر، واستبدل produce بـ Flow للتدفقات التفاعلية.

توصيات عملية لـ Android

في مشاريع Android التي تستخدم Kotlin Coroutines، الزوج الرئيسي من البنّاءات هما launch و async. يُستخدم launch في ViewModel و UseCases لتشغيل الكوروتينات، بينما يُستخدم async للطلبات المتوازية للشبكة أو قاعدة البيانات. المكتبات الحديثة (Ktor, Room) تدعم بالفعل دوال suspend، مما يقلل الحاجة للاستخدام المباشر لـ async.

  • launch — لـ fire-and-forget (تسجيل، تحليلات، تخزين مؤقت)
  • async — للطلبات المتوازية مع دمج النتائج
  • runBlocking — فقط في main() والاختبارات (ليس على خيط Main في الإنتاج)
  • produce — استبدله بـ Flow / SharedFlow / StateFlow

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

ما الفرق بين launch و async في Kotlin؟

launch يُرجع Job ولا يُرجع نتيجة تنفيذ، بينما async يُرجع Deferred<T> — كائن يمكن الحصول على النتيجة منه عبر await(). يُستخدم launch لعمليات fire-and-forget، وasync للمهام التي تُرجع بيانات.

هل يمكن استخدام runBlocking في Activity Android؟

غير موصى به. runBlocking على الخيط الرئيسي يسبب ANR ويحجب واجهة المستخدم. استخدم lifecycleScope.launch داخل Activity و Fragment — إنه حل مدمج بدون حجب.

ماذا يُرجع باني launch؟

باني launch يُرجع كائن Job، الذي يسمح بالتحكم في دورة حياة الكوروتين: الإلغاء (cancel)، انتظار الاكتمال (join)، التحقق من الحالة (isActive, isCompleted, isCancelled).

ما هو Deferred في Kotlin Coroutines؟

Deferred<T> هو وعد غير متزامن بنتيجة، يُرجع بواسطة باني async. يرث من Job ويضيف طرقًا: await() للحصول على النتيجة، getCompleted() للوصول غير الحاجب، و getCompletionExceptionOrNull() للتحقق من الاستثناءات.

كيفية تشغيل launch بتأخير؟

استخدم المعامل CoroutineStart.LAZY: scope.launch(start = CoroutineStart.LAZY) { ... }. ثم استدعِ job.start() أو job.join() للتنفيذ الفعلي. هذا مفيد للتهيئة البطيئة والتنفيذ الشرطي للكوروتينات.

الملخص

  • Coroutine Builder — دالة إنشاء كوروتين تحدد طريقة تنفيذه ونوع إرجاعه
  • launch — الباني الرئيسي للمهام دون إرجاع نتيجة، يُرجع Job لإدارة الكوروتين
  • async — باني للحسابات المتوازية، يُرجع Deferred مع إمكانية await() للنتيجة
  • runBlocking — باني حاجب لـ main() والاختبارات، غير موصى به على الخيط الرئيسي لـ Android
  • التزامن المنظم يضمن الإلغاء التلقائي للكوروتينات الفرعية عند إلغاء النطاق
  • produce مهمل — استخدم Flow أو SharedFlow أو StateFlow للتدفقات التفاعلية
  • اختيار الباني يعتمد على السيناريو: fire-and-forget → launch، بيانات متوازية → async، جسر → runBlocking

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

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

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

اقرأ أيضًا