0Pricing
Kotlin Academy · درس

التنظيف باستخدام finally وNonCancellable

حرّر الموارد في coroutines الملغاة باستخدام finally وwithContext(NonCancellable).

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

الإلغاء وfinally

عند إلغاء coroutine، تطرح نقاط التعليق CancellationException. ويُنفَّذ دائمًا الكود الموجود في كتل finally — لكن finally نفسها تُنفَّذ في سياق coroutine ملغى.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } finally {
            println("finally: cleaning up")
        }
    }
    delay(50)
    job.cancel()
    job.join()
}

المشكلة: التعليق داخل finally

إذا حاولت استخدام delay أو إجراء تعليق آخر داخل finally في coroutine ملغى، فسيُطرح استثناء فورًا لأن السياق ملغى بالفعل.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } finally {
            // delay(100) // throws CancellationException here!
            println("Sync cleanup OK; async cleanup needs NonCancellable")
        }
    }
    delay(50); job.cancel(); job.join()
}

withContext(NonCancellable)

غلّف استدعاءات التعليق داخل finally باستخدام withContext(NonCancellable) لتنفيذها في سياق غير قابل للإلغاء حتى بعد إلغاء الوالد.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } finally {
            withContext(NonCancellable) {
                delay(100)  // OK: runs despite cancellation
                println("Async cleanup complete")
            }
        }
    }
    delay(50); job.cancel(); job.join()
}

متى تستخدم NonCancellable

استخدم NonCancellable من أجل: إغلاق اتصالات قاعدة البيانات، وتفريغ السجلات، وإرسال أحداث التحليلات، أو أي تنظيف غير متزامن يجب أن يكتمل بغض النظر عن الإلغاء.

import kotlinx.coroutines.*
suspend fun closeConnection() {
    println("Closing DB connection...")
    delay(50) // simulate async close
    println("Connection closed")
}
fun main() = runBlocking {
    val job = launch {
        try { delay(1000) }
        finally { withContext(NonCancellable) { closeConnection() } }
    }
    delay(50); job.cancel(); job.join()
}

NonCancellable ليس نطاقًا

إن NonCancellable هو Job يمكنك تمريره إلى withContext. لا تستخدمه مباشرةً كنطاق coroutine — استخدمه فقط لتنظيف الموارد داخل finally.

import kotlinx.coroutines.*
fun main() = runBlocking {
    // WRONG usage:
    // launch(NonCancellable) { ... } // launches a coroutine that cannot be cancelled
    // RIGHT usage:
    launch {
        try { delay(500) }
        finally {
            withContext(NonCancellable) {
                println("Safe cleanup")
            }
        }
    }.also { delay(50); it.cancel(); it.join() }
}

تنظيف موارد متعددة

سَلْسِل عمليات التنظيف المتعددة داخل كتلة واحدة من withContext(NonCancellable).

import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        try {
            delay(1000)
        } finally {
            withContext(NonCancellable) {
                delay(20); println("Step 1: flush cache")
                delay(20); println("Step 2: close socket")
                delay(20); println("Step 3: log audit")
            }
        }
    }.also { delay(50); it.cancel(); it.join() }
}

نمط try/catch/finally

اجمع بين try-catch لمعالجة أخطاء منطق العمل وfinally لتنظيف الموارد — فهذا هو النمط المنظم القياسي.

import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        try {
            delay(100)
            throw RuntimeException("business error")
        } catch (e: RuntimeException) {
            println("Handling: ${e.message}")
        } finally {
            withContext(NonCancellable) {
                println("Always cleanup")
            }
        }
    }.join()
}

ترتيب التنظيف

تُنفَّذ كتل finally بترتيب عكسي لترتيب التداخل. وتُنفَّذ try-finally الخارجية بعد الكتل الداخلية، تمامًا كما في كود Java/Kotlin العادي.

import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        try {
            try {
                delay(1000)
            } finally { println("Inner cleanup") }
        } finally { println("Outer cleanup") }
    }.also { delay(50); it.cancel(); it.join() }
}

استخدام use() مع موارد AutoCloseable

بالنسبة إلى موارد AutoCloseable، استخدم الامتداد use { } في Kotlin، إذ يستدعي close() تلقائيًا حتى عند حدوث استثناءات أو إلغاء.

import kotlinx.coroutines.*
class Connection : AutoCloseable {
    init { println("Opened") }
    override fun close() { println("Closed") }
    suspend fun query(): String { delay(50); return "result" }
}
fun main() = runBlocking {
    val result = Connection().use { it.query() }
    println(result)
}

تسجيل سبب الإلغاء

استرجع رسالة الإلغاء من CancellationException داخل catch أو finally لتسجيل سبب توقف coroutine.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } catch (e: CancellationException) {
            println("Cancelled: ${e.message}")
            throw e
        }
    }
    delay(50)
    job.cancel("user navigated away")
    job.join()
}

تنظيف CoroutineScope باستخدام invokeOnCompletion

يسجّل Job.invokeOnCompletion دالة رد اتصال تُنفَّذ عند انتهاء المهمة لأي سبب — وهو بديل عن finally لتنظيف الموارد على مستوى الوالد.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        delay(1000)
    }
    job.invokeOnCompletion { cause ->
        if (cause != null) println("Ended with: ${cause::class.simpleName}")
        else println("Completed normally")
    }
    delay(50)
    job.cancel()
    job.join()
}

تحقق سريع

ما الغرض من withContext(NonCancellable) داخل كتلة finally؟

مراجعة

تُنفَّذ finally دائمًا عند الإلغاء. غلّف استدعاءات التعليق داخل finally باستخدام withContext(NonCancellable) حتى تكتمل رغم الإلغاء. استخدم use { } مع موارد AutoCloseable.

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

هل درس «التنظيف باستخدام finally وNonCancellable» مجاني؟

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

ماذا ستتعلم في «التنظيف باستخدام finally وNonCancellable»؟

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

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

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

كم من الوقت يستغرق درس «التنظيف باستخدام finally وNonCancellable»؟

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

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

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

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

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