0Pricing
Kotlin Academy · درس

انتشار الاستثناءات مع async/await

تعرّف على كيفية انتشار الاستثناءات من async ومتى تستخدم try-await.

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

يخزّن async الاستثناءات

بخلاف launch، يخزّن async الاستثناء في Deferred المُعادة. ولا يُعاد رميه إلا عند استدعاء await().

import kotlinx.coroutines.*
fun main() = runBlocking {
    val deferred = async {
        throw RuntimeException("async error")
    }
    try {
        deferred.await()  // exception rethrown here
    } catch (e: RuntimeException) {
        println("Caught: ${e.message}")
    }
}

استثناء من دون await()

إذا لم تستدعِ await() مطلقًا، فسيُهمَل الاستثناء بصمت مع أب من نوع Job عادي. يُفضّل استخدام supervisorScope مع الانتظار دائمًا.

import kotlinx.coroutines.*
fun main() = runBlocking {
    // Exception stored in deferred, never retrieved:
    val d = async { throw RuntimeException("lost exception") }
    delay(100)  // d has failed — exception never surfaced
    println("d.isCancelled: ${d.isCancelled}")
}

async ضمن coroutineScope

ضمن coroutineScope عادي، إذا رمت مهمة async فرعية استثناءً وانتشر الاستثناء دون التقاطه، فسيُلغي النطاق وجميع المهام الشقيقة.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        coroutineScope {
            val a = async { "A" }
            val b = async { throw RuntimeException("B failed") }
            println(a.await())
            println(b.await())  // propagates, cancels scope
        }
    } catch (e: RuntimeException) {
        println("Scope failed: ${e.message}")
    }
}

async ضمن supervisorScope

ضمن supervisorScope، لا تنتشر الاستثناءات الصادرة عن async إلى المهام الشقيقة. يجب تغليف كل await()` على حدة.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val a = async { "A" }
        val b = async { throw RuntimeException("B failed") }
        println(a.await())
        try { println(b.await()) }
        catch (e: RuntimeException) { println("b failed: ${e.message}") }
    }
}

awaitAll لعدة عمليات async

ينتظر awaitAll(d1, d2, d3) جميع القيم المؤجلة. وإذا فشلت إحداها، فإنه يرمي الاستثناء فورًا ويلغي القيم الأخرى.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        val results = awaitAll(
            async { "result1" },
            async { throw RuntimeException("task 2 failed") },
            async { "result3" }
        )
        println(results)
    } catch (e: Exception) {
        println("awaitAll failed: ${e.message}")
    }
}

runCatching مع async

غلّف كل استدعاء لـ await() داخل runCatching لجمع النتائج والأخطاء دون إيقاف بقية العمليات عند أول فشل.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val deferreds = listOf(
            async { "A" },
            async { throw RuntimeException("B") },
            async { "C" }
        )
        val results = deferreds.map { runCatching { it.await() } }
        results.forEach { println(it) }
    }
}

المهام المتوازية مع تجميع الأخطاء

اجمع جميع حالات الفشل من مهام async المتوازية وأبلغ عنها معًا بدلًا من الإخفاق السريع عند أول خطأ.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val tasks = listOf("a", "b", "c")
    val results = supervisorScope {
        tasks.map { t ->
            async {
                if (t == "b") throw RuntimeException("b failed")
                t.uppercase()
            }
        }.map { runCatching { it.await() } }
    }
    val errors = results.filter { it.isFailure }
    val successes = results.mapNotNull { it.getOrNull() }
    println("Success: $successes, Errors: ${errors.size}")
}

الحفاظ على نوع الاستثناء

تحافظ الاستثناءات المرمية داخل async على نوعها. التقط نوع الاستثناء المحدد عند await() لمعالجة الأخطاء بدقة.

import kotlinx.coroutines.*
class NetworkException(msg: String) : RuntimeException(msg)
fun main() = runBlocking {
    val d = async { throw NetworkException("timeout") }
    try {
        d.await()
    } catch (e: NetworkException) {
        println("Network error: ${e.message}")
    } catch (e: Exception) {
        println("Other error: ${e.message}")
    }
}

Deferred.getCompleted() للتحقق دون تعليق

بعد join()، استخدم getCompleted() لاسترداد النتيجة بشكل متزامن. فهو يرمي استثناءً إذا فشلت القيمة المؤجلة.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val d = async { 42 }
    d.join()  // wait without caring about result
    try {
        val result = d.getCompleted()  // synchronous — no suspend
        println("Result: $result")
    } catch (e: Exception) {
        println("Failed: ${e.message}")
    }
}

التزامن المنظم وasync

ضمن التزامن المنظم، تكون قيمة async المؤجلة مرتبطة دائمًا بنطاقها الأب. وعند إلغاء النطاق، تُلغى القيمة المؤجلة أيضًا، فلا توجد مهام يتيمة.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val scope = CoroutineScope(SupervisorJob())
    val d = scope.async {
        delay(1000)
        "result"
    }
    scope.cancel()  // cancels d too
    try { d.await() }
    catch (e: CancellationException) { println("Cancelled as expected") }
}

أفضل ممارسة: انتظر دائمًا

استدعِ await() دائمًا لكل قيمة مؤجلة من async، حتى إن لم تكن بحاجة إلى النتيجة. فهذا يكشف الاستثناءات ويمنع حالات الفشل الصامتة.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val d = async {
            // do background work
            "done"
        }
        // Always await:
        val result = runCatching { d.await() }
        println(result)
    }
}

تحقق سريع

متى يظهر الاستثناء الصادر عن كتلة async { throw ... }؟

مراجعة

يخزّن async الاستثناءات في Deferred، وتظهر عند await(). ضمن coroutineScope، يؤدي انتشار الاستثناء إلى إلغاء المهام الشقيقة. أما ضمن supervisorScope، فيجب أن يعالج كل await فشله بنفسه. انتظر دائمًا كل قيمة مؤجلة.

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

هل درس «انتشار الاستثناءات مع async/await» مجاني؟

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

ماذا ستتعلم في «انتشار الاستثناءات مع async/await»؟

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

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

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

كم من الوقت يستغرق درس «انتشار الاستثناءات مع async/await»؟

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

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

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

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

  1. ‏SupervisorJob مقابل Job: عزل حالات الفشل
  2. ‏CoroutineExceptionHandler: المعالج العام للاستثناءات غير الملتقطة
  3. انتشار الاستثناءات مع async/await
  4. تصميم معماريات Coroutines مرنة
← العودة إلى Kotlin Academy