الف-:coroutineScope
یک suspend function است که یک بلاک کد را به عنوان پارامتر میگیرد و یک کوروتین را ایجاد میکند.خب در مورد coroutineScope قبلا هم صحبت کرده بودیم .بنابرین
وظیفهی coroutineScope
این است که تا زمانی که کلیهی کوروتینهای داخلی اجرا نشدهاند، اجازه نمیدهد که خروجی داده شود.
اگر یکی از کوروتینهای داخلی با خطا روبرو شود، تمامی کوروتینهای دیگر در coroutineScope
لغو میشوند و این کوروتین همچنین با یک CancellationException
کنسل میشود.
supervisorScope
نیز مانند coroutineScope
یک suspend function است که یک بلاک کد را به عنوان پارامتر میگیرد و یک کوروتین را ایجاد میکند. با این تفاوت که supervisorScope
اجازه میدهد که کوروتینهای داخلی به طور مستقل از یکدیگر اجرا شوند. به این معنی که اگر یکی از کوروتینهای داخلی با خطا مواجه شود، فقط آن کوروتین لغو میشود و بقیه ادامه مییابند.با استفاده از این دو تابع، میتوانید رفتار مناسبی برای مدیریت خطاها و اجرای کوروتینها در برنامهی خود ایجاد کنید.
خب حالا مثال بزنیم که بیشتر درک کنیم :
ابتدا فرض کنید میخواهید یک بلاک کد را با استفاده از کوروتینها اجرا کنید و اطمینان حاصل کنید که همهی کوروتینها با خطا مواجه نشدهاند. ابتدا، با استفاده از coroutineScope
این کار را انجام میدهیم:
suspend fun main() {
try {
coroutineScope {
val job1 = launch {
delay(1000)
println("Task 1 completed")
}
val job2 = launch {
delay(2000)
println("Task 2 completed")
}
// Wait for all child coroutines to complete
job1.join()
job2.join()
println("All tasks completed successfully")
}
} catch (e: Exception) {
println("An error occurred: ${e.message}")
}
}
خروجی کد بالا به صورت زیر میباشد :
Task 1 completed
Task 2 completed
All tasks completed successfully
در این کد، دو کوروتین (job1
و job2
) در یک coroutineScope
اجرا میشوند. ابتدا کوروتین job1
با یک تأخیر 1000 میلیثانیه اجرا شده و پس از آن پیام "Task 1 completed" را چاپ میکند. سپس کوروتین job2
با یک تأخیر 2000 میلیثانیه اجرا شده و پس از آن پیام "Task 2 completed" را چاپ میکند. سپس با استفاده از join()
انتظار میرود تا هر دو کوروتین به پایان برسند و پیام "All tasks completed successfully" چاپ میشود.
حالا اگر یکی از کوروتینها با خطا مواجه شود، کل بلاک کد coroutineScope
به طور کامل لغو میشود:
suspend fun main() {
try {
coroutineScope {
val job1 = launch {
delay(1000)
println("Task 1 completed")
}
val job2 = launch {
delay(2000)
println("Task 2 completed")
throw Exception("Error in task 2")
}
// Wait for all child coroutines to complete
job1.join()
job2.join()
println("All tasks completed successfully")
}
} catch (e: Exception) {
println("An error occurred: ${e.message}")
}
}
حالا خروجی کد زیر به صورت زیر است :
Task 1 completed
Task 2 completed
An error occurred: Error in task 2
در این کد، دو کوروتین (job1
و job2
) در یک coroutineScope
اجرا میشوند. ابتدا کوروتین job1
با یک تأخیر 1000 میلیثانیه اجرا شده و پس از آن پیام "Task 1 completed" را چاپ میکند. سپس کوروتین job2
با یک تأخیر 2000 میلیثانیه اجرا شده و پیام "Task 2 completed" را چاپ میکند. اما سپس یک استثناء (Exception
) در job2
پرتاب میشود با پیام "Error in task 2".
زمانی که استثناء در job2
پرتاب میشود، اجرای کد از داخل coroutineScope
خارج شده و به بلاک catch که در main تعریف شده است منتقل میشود. از طریق این بلاک catch، پیام "An error occurred: Error in task 2" چاپ میشود.
حالا اگر میخواهید تنها کوروتینی که با خطا مواجه شده است را لغو کنید و سایر کوروتینها ادامه دهند، از supervisorScope
استفاده کنید:
suspend fun main() {
try {
supervisorScope {
val job1 = launch {
delay(1000)
println("Task 1 completed")
}
val job2 = launch {
delay(2000)
println("Task 2 completed")
throw Exception("Error in task 2")
}
// Wait for all child coroutines to complete
job1.join()
job2.join()
println("All tasks completed successfully")
}
} catch (e: Exception) {
println("An error occurred: ${e.message}")
}
}
خروجی کد بالا به صورت زیر است :
Task 1 completed
Task 2 completed
All tasks completed successfully
در این کد، دو کوروتین (job1
و job2
) در یک supervisorScope
اجرا میشوند. ابتدا کوروتین job1
با یک تأخیر 1000 میلیثانیه اجرا شده و پس از آن پیام "Task 1 completed" را چاپ میکند. سپس کوروتین job2
با یک تأخیر 2000 میلیثانیه اجرا شده و پیام "Task 2 completed" را چاپ میکند. اما سپس یک استثناء (Exception
) در job2
پرتاب میشود با پیام "Error in task 2".
با استفاده از supervisorScope
، خطاهای مربوط به کوروتینهای فرزند به کوروتین والد منتقل نمیشوند. بنابراین، بلاک catch در main فراخوانی نخواهد شد و برنامه به اجرای خود ادامه میدهد.
خب حالا ببینیم SupervisorJob
چی هست !!
خب Job یک مفهوم اصلی در کوروتینها است و یک نشانگر برای یک کار مشخص است.
هنگامی که شما یک کوروتین را ایجاد میکنید، یک Job
به عنوان نتیجه برگردانده میشود که میتوانید از آن برای لغو کردن کار، پیگیری وضعیت آن، و غیره استفاده کنید.
Job
است که برای ایجاد یک محیط کاری (supervisor scope) استفاده میشود که کوروتینهای مستقل از یکدیگر راه اندازی میشوند.SupervisorJob
، اگر یکی از کوروتینها با خطا مواجه شود، فقط آن کوروتین لغو میشود و سایر کوروتینها ادامه مییابند.حالا با استفاده از مثال، این تفاوت را بیان میکنیم:
suspend fun main() {
try {
val parentJob = Job() // Creating a parent Job
val child1 = CoroutineScope(parentJob).launch {
delay(1000)
println("Task 1 completed")
}
val child2 = CoroutineScope(parentJob).launch {
delay(2000)
println("Task 2 completed")
throw Exception("Error in task 2")
}
// Wait for all child coroutines to complete
parentJob.join()
println("All tasks completed successfully")
} catch (e: Exception) {
println("An error occurred: ${e.message}")
}
}
در این مثال، هر دو کوروتین child1
و child2
به عنوان فرزندان یک Job
ایجاد شدهاند. بنابراین، اگر یکی از آنها با خطا مواجه شود، تمام کوروتینها در parentJob
لغو میشوند.هر دو کروتین یک تاخیر دارند و پس از اتمام تاخیر، پیامی را چاپ میکنند. با توجه به این که کروتین دوم یک استثناء را پرتاب میکند، برنامه خطا خواهد داشت و عبارت "An error occurred: Error in task 2" را چاپ میکند.
حالا همین کد را با استفاده از SupervisorJob
اصلاح میکنیم:
suspend fun main() {
try {
val parentJob = SupervisorJob() // Creating a parent SupervisorJob
val child1 = CoroutineScope(parentJob).launch {
delay(1000)
println("Task 1 completed")
}
val child2 = CoroutineScope(parentJob).launch {
delay(2000)
println("Task 2 completed")
throw Exception("Error in task 2")
}
// Wait for all child coroutines to complete
parentJob.join()
println("All tasks completed successfully")
} catch (e: Exception) {
println("An error occurred: ${e.message}")
}
}
در اینجا از SupervisorJob
به جای Job
برای ایجاد والد استفاده شده است.وظیفه SupervisorJob
تفاوتی با Job
دارد. وقتی که یک کروتین کودک در یک SupervisorJob
شکست میخورد (مثل پرتاب یک استثناء)، تنها کروتین متاثر میشود و کروتینهای دیگر تحت مدیریت همچنان ادامه مییابند. بنابراین، در اینجا کروتین دوم یک استثناء را پرتاب میکند، اما کروتین اول همچنان ادامه دارد. پس از اتمام هر دو کروتین، پیام "All tasks completed successfully" چاپ میشود.
بنابراین، خروجی برنامه به صورت زیر خواهد بود:
Task 1 completed
Task 2 completed
All tasks completed successfully