SupervisorJob مقابل Job: عزل حالات الفشل
استخدم SupervisorJob لمنع فشل أحد الأبناء من إلغاء الأشقاء.
SupervisorJob مقابل Job: عزل حالات الفشل درس مجاني في Kotlin Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Kotlin Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Kotlin Academy 4 دروس في المجموع.
انتشار فشل Job الافتراضي
مع Job عادي، يؤدي فشل أحد الأبناء إلى إلغاء الأب، الذي يلغي بدوره جميع الإخوة. ويؤدي فشل واحد إلى إسقاط التسلسل الهرمي بأكمله.
import kotlinx.coroutines.*
fun main() = runBlocking {
try {
coroutineScope {
launch { delay(50); throw RuntimeException("child 1 failed") }
launch { delay(1000); println("child 2 — never prints") }
}
} catch (e: RuntimeException) {
println("Caught: ${e.message}")
}
}نظرة عامة على SupervisorJob
يغيّر SupervisorJob هذه القاعدة: إذ لا يؤدي فشل أحد الأبناء إلى إلغاء الإخوة أو الأب. ويفشل كل ابن بصورة مستقلة.
import kotlinx.coroutines.*
fun main() = runBlocking {
val supervisor = SupervisorJob()
val scope = CoroutineScope(coroutineContext + supervisor)
scope.launch { throw RuntimeException("child 1 failed") }
scope.launch { delay(100); println("child 2 still runs") }
delay(200)
supervisor.cancel()
}منشئ supervisorScope
تنشئ supervisorScope { } نطاقًا يكون فيه SupervisorJob هو الأب. وهذه هي الطريقة الاصطلاحية لتشغيل أبناء مستقلين.
import kotlinx.coroutines.*
fun main() = runBlocking {
supervisorScope {
val job1 = launch {
throw RuntimeException("job1 failed")
}
val job2 = launch {
delay(100)
println("job2 succeeded")
}
job1.join() // wait for job1 (it failed)
job2.join() // job2 is unaffected
}
}التقاط حالات فشل الأبناء
مع supervisorScope، يعالج كل ابن الاستثناء الخاص به. ويجب التقاط الاستثناءات غير المعالجة في الأبناء لكل ابن على حدة.
import kotlinx.coroutines.*
fun main() = runBlocking {
supervisorScope {
val result = async {
delay(50)
throw RuntimeException("async failed")
}
try {
result.await()
} catch (e: RuntimeException) {
println("Caught from async: ${e.message}")
}
println("Scope continues")
}
}Job مقابل SupervisorJob عند إنشاء النطاق
مرّر SupervisorJob() عند إنشاء CoroutineScope لخدمة أو ViewModel ينبغي أن يستمر رغم فشل الأبناء الفرديين.
import kotlinx.coroutines.*
class MyService {
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
fun startTask(name: String) {
scope.launch {
if (name == "fail") throw RuntimeException("$name failed")
println("$name done")
}
}
fun stop() = scope.cancel()
}
fun main() = runBlocking {
val svc = MyService()
svc.startTask("fail")
svc.startTask("ok")
delay(100)
svc.stop()
}يستخدم viewModelScope SupervisorJob
يعتمد viewModelScope في Android على SupervisorJob. ولا يؤدي فشل عملية launch واحدة إلى إلغاء coroutines الأخرى في ViewModel.
import kotlinx.coroutines.*
// Android ViewModel internals:
// val viewModelScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
// Each launch is independent — one failure doesn't crash the ViewModel
fun main() = runBlocking {
println("viewModelScope = SupervisorJob + Main")
}التفكيك المتوازي باستخدام supervisorScope
استخدم supervisorScope مع async للمهام المتوازية التي قد يفشل بعضها دون التأثير في المهام الأخرى.
import kotlinx.coroutines.*
fun main() = runBlocking {
val results = supervisorScope {
val a = async { delay(50); "result-A" }
val b = async { throw RuntimeException("B failed") }
val c = async { delay(30); "result-C" }
listOf(
runCatching { a.await() },
runCatching { b.await() },
runCatching { c.await() }
)
}
results.forEach { println(it) }
}متى تستخدم Job العادي
استخدم Job العادي (coroutineScope) عندما يجب أن تنجح جميع المهام الفرعية معًا؛ إذ ينبغي لفشل واحدة منها أن يلغي المجموعة بأكملها. يشبه ذلك المعاملة: إما أن تنجح كلها وإما ألا تنجح أيٌّ منها.
import kotlinx.coroutines.*
fun main() = runBlocking {
try {
coroutineScope { // regular Job
val a = async { delay(50); "A" }
val b = async { throw RuntimeException("B failed") }
println(a.await())
println(b.await()) // throws — cancels a too
}
} catch (e: RuntimeException) {
println("Transaction failed: ${e.message}")
}
}انتشار الاستثناءات في supervisorScope
في supervisorScope، ينتشر الاستثناء إلى النطاق فقط إذا خرج من المستوى الأعلى للمهمة الفرعية، أي عند عدم وجود try-catch داخل المهمة الفرعية. ولا يراه النطاق الأب.
import kotlinx.coroutines.*
fun main() = runBlocking {
supervisorScope {
// Exception caught inside child — scope unaffected
launch {
try { throw RuntimeException("handled") }
catch (e: Exception) { println("Child caught: ${e.message}") }
}
delay(100)
println("Scope survived")
}
}CoroutineExceptionHandler مع SupervisorJob
ثبّت CoroutineExceptionHandler في نطاق إشرافي لتسجيل حالات فشل المهام الفرعية غير المعالجة أو الاستجابة لها، دون التسبب في انهيار النطاق.
import kotlinx.coroutines.*
fun main() = runBlocking {
val handler = CoroutineExceptionHandler { _, e ->
println("Uncaught: ${e.message}")
}
val scope = CoroutineScope(SupervisorJob() + handler)
scope.launch { throw RuntimeException("unhandled failure") }
scope.launch { delay(100); println("still running") }
delay(200)
scope.cancel()
}ملخص عزل حالات الفشل
Job: تفشل مهمة فرعية واحدة ← تفشل جميع المهام. SupervisorJob: تفشل المهام الفرعية بشكل مستقل. استخدم النطاق الإشرافي للخدمات وViewModels، وJob العادي للمجموعات التي تعمل كمعاملات.
import kotlinx.coroutines.*
// Decision table:
// coroutineScope { } -> Job: all-or-nothing
// supervisorScope { } -> SupervisorJob: independent failures
// CoroutineScope(SupervisorJob()) -> long-lived service
fun main() = runBlocking { println("Pick the right job for the right scope") }تحقق سريع
أي نطاق يمنع فشل مهمة فرعية من إلغاء المهام الشقيقة؟
مراجعة
Job: ينتشر الفشل إلى المهام الشقيقة. SupervisorJob/supervisorScope: يُعزل الفشل لكل مهمة فرعية. استخدم supervisorScope للمهام المتوازية المستقلة، وcoroutineScope العادي للمجموعات التي تعمل كمعاملات.
تعلم Kotlin مع معلم ذكاء اصطناعي — مجانًا
اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.
- الدورات
- 51
- الدروس
- 203
الأسئلة الشائعة
هل درس «SupervisorJob مقابل Job: عزل حالات الفشل» مجاني؟
نعم — نص درس «SupervisorJob مقابل Job: عزل حالات الفشل» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Kotlin Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Kotlin Academy 4 دروس في المجموع.
ماذا ستتعلم في «SupervisorJob مقابل Job: عزل حالات الفشل»؟
استخدم SupervisorJob لمنع فشل أحد الأبناء من إلغاء الأشقاء. تتمرن على Kotlin Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Kotlin Academy؟
لا تُشترط خبرة سابقة. Kotlin Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «SupervisorJob مقابل Job: عزل حالات الفشل»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Kotlin Academy هذا؟
نعم. كل درس في Kotlin Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- SupervisorJob مقابل Job: عزل حالات الفشل
- CoroutineExceptionHandler: المعالج العام للاستثناءات غير الملتقطة
- انتشار الاستثناءات مع async/await
- تصميم معماريات Coroutines مرنة