0Pricing
Kotlin Academy · درس

انتشار الإلغاء في التسلسلات الهرمية لـ Coroutine

تعرّف على كيفية انتقال الإلغاء عبر علاقات coroutines الأبوية والتابعة.

انتشار الإلغاء في التسلسلات الهرمية لـ Coroutine درس مجاني في Kotlin Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Kotlin Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Kotlin Academy 4 دروس في المجموع.

علاقة الوالد بالطفل

عندما يطلق coroutine آخر باستخدام launch، ينضم الطفل إلى Job الخاص بالوالد. ويؤدي إلغاء الوالد إلى إلغاء جميع الأطفال.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val parent = launch {
        launch { delay(1000); println("child 1") }
        launch { delay(1000); println("child 2") }
        delay(1000)
        println("parent")
    }
    delay(50)
    parent.cancel()
    parent.join()
    println("All cancelled")
}

فشل الطفل يلغي الوالد

إذا طرح طفل استثناءً لا يتعلق بالإلغاء، فإنه يلغي الوالد وجميع الأشقاء — وهذا هو السلوك الافتراضي لـ Job.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        coroutineScope {
            launch {
                delay(50)
                throw RuntimeException("child failed")
            }
            launch {
                delay(1000)
                println("sibling — never prints")
            }
        }
    } catch (e: RuntimeException) {
        println("Caught: ${e.message}")
    }
}

الإلغاء لا ينتشر إلى الأعلى

لا يؤدي إلغاء الطفل إلى إلغاء الوالد. فالاستثناءات غير المعالجة وحدها تفعل ذلك. ويمكن للوالد إلغاء الطفل في أي وقت.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val parent = launch {
        val child = launch {
            delay(1000)
            println("child done")
        }
        delay(50)
        child.cancel()  // cancels child
        child.join()
        println("parent still running")  // parent is fine
    }
    parent.join()
}

coroutineScope في مقابل GlobalScope

ينشئ coroutineScope نطاقًا تابعًا: إذ تنتشر عمليات الإلغاء والأخطاء. أما GlobalScope فينشئ coroutines يتيمة بلا والد — فتجنّب استخدامه في بيئة الإنتاج.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        coroutineScope {
            launch { delay(50); throw RuntimeException("error") }
        }
    } catch (e: Exception) {
        println("coroutineScope propagated: ${e.message}")
    }
    // GlobalScope.launch { } would NOT propagate to runBlocking
}

إلغاء شجرة فرعية

يعيد كل من launch وasync كائن Job. ألغِ المهمة لإلغاء شجرتها الفرعية بالكامل، بما في ذلك أي عمليات إطلاق متداخلة.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val root = launch {
        launch {
            launch { delay(1000); println("deep") }
            delay(1000)
        }
    }
    delay(50)
    root.cancel()
    root.join()
    println("Whole subtree cancelled")
}

استدعاء join() بعد cancel()

استدعِ join() دائمًا بعد cancel() للانتظار حتى يتوقف coroutine وأطفاله بالكامل قبل المتابعة.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try { delay(1000) }
        finally { println("cleanup ran") }
    }
    delay(50)
    job.cancel()
    job.join()  // waits for finally to complete
    println("Proceeded after join")
}

cancelAndJoin()

إن job.cancelAndJoin() دالة مساعدة تلغي المهمة ثم تنتظر انضمامها، وهي مكافئة لـ cancel() + join().

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try { delay(1000) }
        finally { println("cleanup") }
    }
    delay(50)
    job.cancelAndJoin()  // cancel + join in one call
    println("Done")
}

الانتشار عبر async

مع async، تُخزَّن الاستثناءات في Deferred وتُطرح عند استدعاء await(). ومع ذلك، تظل تنتشر إلى الوالد إذا لم تُعالج.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        coroutineScope {
            val d = async { throw RuntimeException("async fail") }
            d.await()  // rethrows here
        }
    } catch (e: RuntimeException) {
        println("Caught from async: ${e.message}")
    }
}

ضمان التزامن المنظم

يضمن التزامن المنظم أنه عند انتهاء النطاق يكون جميع أطفاله قد انتهوا. ولا تتسرب أي coroutines — فإما أن تكتمل أو تُلغى.

import kotlinx.coroutines.*
suspend fun doWork() = coroutineScope {
    launch { delay(100); println("work 1") }
    launch { delay(200); println("work 2") }
    // both children complete before doWork returns
}
fun main() = runBlocking {
    doWork()
    println("All work done")
}

مخطط انتشار الإلغاء

التسلسل الهرمي: إلغاء الوالد ← إلغاء جميع الأطفال. إلغاء الطفل ← تلك الشجرة الفرعية فقط. استثناء الطفل ← إلغاء الوالد ← إلغاء الأشقاء.

import kotlinx.coroutines.*
fun main() = runBlocking {
    // Parent
    launch {
        val c1 = launch { delay(1000); println("c1") }  // child 1
        val c2 = launch { delay(1000); println("c2") }  // child 2
        delay(50)
        c1.cancel()  // only c1 cancelled
        c1.join()
        println("c2 still active: ${c2.isActive}")
        c2.cancelAndJoin()
    }.join()
}

دورة حياة CoroutineScope في Android

في Android، يُلغى viewModelScope عند إزالة ViewModel. وتُلغى جميع coroutines التي أُطلقت تلقائيًا — ولا حاجة إلى تنظيف يدوي.

import kotlinx.coroutines.*
// Pseudocode:
// class MyViewModel : ViewModel() {
//     fun load() = viewModelScope.launch {
//         val data = repo.fetch() // cancelled if VM cleared
//         _state.value = data
//     }
// }
fun main() = runBlocking { println("viewModelScope cancels on ViewModel.onCleared()") }

تحقق سريع

ماذا يحدث للـ coroutines الشقيقة عندما يطرح أحد الأطفال استثناءً غير معالج ضمن Job عادي؟

مراجعة

ينتشر إلغاء الوالد إلى جميع الأطفال. وينتشر فشل الطفل إلى الوالد والأشقاء مع Job العادي. استخدم cancelAndJoin() لإنهاء التنفيذ بطريقة نظيفة. ويضمن التزامن المنظم عدم تسرب أي coroutine.

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

هل درس «انتشار الإلغاء في التسلسلات الهرمية لـ Coroutine» مجاني؟

نعم — نص درس «انتشار الإلغاء في التسلسلات الهرمية لـ Coroutine» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Kotlin Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Kotlin Academy 4 دروس في المجموع.

ماذا ستتعلم في «انتشار الإلغاء في التسلسلات الهرمية لـ Coroutine»؟

تعرّف على كيفية انتقال الإلغاء عبر علاقات coroutines الأبوية والتابعة. تتمرن على Kotlin Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Kotlin Academy؟

لا تُشترط خبرة سابقة. Kotlin Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «انتشار الإلغاء في التسلسلات الهرمية لـ Coroutine»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Kotlin Academy هذا؟

نعم. كل درس في Kotlin Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. الإلغاء التعاوني: ‏isActive وensureActive
  2. ‏withTimeout وwithTimeoutOrNull
  3. التنظيف باستخدام finally وNonCancellable
  4. انتشار الإلغاء في التسلسلات الهرمية لـ Coroutine
← العودة إلى Kotlin Academy