narjes Mansoori
narjes Mansoori
خواندن ۳ دقیقه·۱۰ ماه پیش

مفهوم Exception Handling در کوروتین ها

در Coroutines، مدیریت استثناء (Exception Handling) یک موضوع مهمه که باید آن را یاد بگیریم. خب حالا در اینجا به برخی روش‌های مدیریت استثناء در زمان استفاده از launch و async می‌پردازیم.سعی میکنیم با مثال پیش بریم که درک بیشتری از مطالب داشته باشیم.

زمان استفاده از launch

زمانی که از launch استفاده می‌کنیم، می‌توانیم با استفاده از بلاک try-catch یا یک exception handler خطا را مدیریت کنیم.

استفاده از بلاک try-catch:

GlobalScope.launch(Dispatchers.Main) {
try {
fetchUserAndSaveInDatabase()
} catch (exception: Exception) {
Log.d(TAG, "$exception handled !")
}
}

در مثال بالا اگر خطایی در fetchUserAndSaveInDatabase رخ دهد در بلاک catch مدریت میشود.خب حالا اگر نخواهیم از try - catch استفاده کنیم میتوانیم به صورت زیر از CoroutineExceptionHandler استفاده کنیم .

استفاده از Exception Handler:

ابتدا باید یک exception handler ایجاد کنیم:

val handler = CoroutineExceptionHandler { _, exception ->
Log.d(TAG, "$exception handled !")
}

سپس می‌توانیم handler را به این صورت به launch پاس دهیم:

kotlinCopy codeGlobalScope.launch(Dispatchers.Main + handler) {
fetchUserAndSaveInDatabase()
}

زمان استفاده از async

زمان استفاده از async نیز باید با استفاده از بلاک try-catch خطا را مدیریت کنیم.

val deferredUser = GlobalScope.async {
fetchUser()
}
try {
val user = deferredUser.await()
} catch (exception: Exception) {
Log.d(TAG, "$exception handled !")
}

در موارد استفاده واقعی، ممکن است بخواهیم با شرایط خاصی روبه‌رو شویم. به عنوان مثال، اگر بخواهیم در صورت خطا یک لیست خالی را برگردانیم و با پاسخ دیگری ادامه دهیم، می‌توانیم بلاک try-catch را برای هر تابع فراخوانی شده اعمال کنیم.

kotlinCopy codelaunch {
val users = try {
getUsers()
} catch (e: Exception) {
emptyList<User>()
}
val moreUsers = try {
getMoreUsers()
} catch (e: Exception) {
emptyList<User>()
}
}

خب حالا قطعه کد بالا مشکل ما رو حل کرد اما ممکن هست بخواهیم تمامی توابع را همزمان اجرا کنیم، باید از coroutineScope به صورت زیر استفاده کنیم:

launch {
try {
coroutineScope {
val usersDeferred = async { getUsers() }
val moreUsersDeferred = async { getMoreUsers() }
val users = usersDeferred.await()
val moreUsers = moreUsersDeferred.await()
}
} catch (exception: Exception) {
Log.d(TAG, "$exception handled !")
}
}

خب در کد بالا اگر یکی از دورخواست ها به خطا بخورد ما وارد بلاک catch میشویم و دیگه بقیه درخواست های ما اجرا نخوهد شد ولی اگر بخواهیم در صورت خطا درخواست های دیگه ادامه داده شوند و باز هم با پاسخ‌های دیگر ادامه دهیم و تمامی توابع را همزمان اجرا کنیم، باید از supervisorScope استفاده کنیم.به مثال زیر توجه کنید:

kotlinCopy codelaunch {
supervisorScope {
val usersDeferred = async { getUsers() }
val moreUsersDeferred = async { getMoreUsers() }
val users = try {
usersDeferred.await()
} catch (e: Exception) {
emptyList<User>()
}
val moreUsers = try {
moreUsersDeferred.await()
} catch (e: Exception) {
emptyList<User>()
}
}
}

نتیجه‌گیری

  • در زمان استفاده از launch می‌توانیم با استفاده از try-catch یا CoroutineExceptionHandler به مدیریت استثناء بپردازیم.
  • در زمان استفاده از async، علاوه بر try-catch، دو گزینه برای مدیریت همزمانی داریم: coroutineScope و supervisorScope.
  • با استفاده از supervisorScope، در صورت خطا اجرای سایر وظایف ادامه خواهد داشت، در حالی که coroutineScope وقتی یکی از وظایف آن خطا داشته باشد، کل وظایف را لغو می‌کند.

آشنایی کامل با مفاهیم supervisorScope و supervisorJob در کوروتین ها

try catchexceptioncoroutineکوروتین
Android Developer
شاید از این پست‌ها خوشتان بیاید