در Coroutines، مدیریت استثناء (Exception Handling) یک موضوع مهمه که باید آن را یاد بگیریم. خب حالا در اینجا به برخی روشهای مدیریت استثناء در زمان استفاده از launch
و async
میپردازیم.سعی میکنیم با مثال پیش بریم که درک بیشتری از مطالب داشته باشیم.
launch
زمانی که از launch
استفاده میکنیم، میتوانیم با استفاده از بلاک try-catch یا یک exception handler خطا را مدیریت کنیم.
GlobalScope.launch(Dispatchers.Main) {
try {
fetchUserAndSaveInDatabase()
} catch (exception: Exception) {
Log.d(TAG, "$exception handled !")
}
}
در مثال بالا اگر خطایی در fetchUserAndSaveInDatabase رخ دهد در بلاک catch مدریت میشود.خب حالا اگر نخواهیم از try - catch استفاده کنیم میتوانیم به صورت زیر از CoroutineExceptionHandler استفاده کنیم .
ابتدا باید یک 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 در کوروتین ها