14

Coroutine؛ چندکاری بدون قفل کردن برنامه

برنامه منتظر اینترنت بمونه ولی خشک نزنه؟ بفرما کوروتین.

درس 14 از 20

Coroutine چیه؟

کوروتین یه محاسبه‌ی suspendableه؛ یعنی می‌تونه وسط کار وایسه بدون اینکه نخ سیستم رو بی‌خودی قفل کنه. برای شبکه، دیتابیس، تایمر و کارهای async خیلی مهمه. خلاصه همزمانی بدون جنگ اعصاب callbackها.

مثال را با چند مقدار دیگر هم امتحان کن؛ چون برنامه‌نویسی با نگاه کردن یاد گرفته نمی‌شود، با خراب کردن و درست کردن جا می‌افتد.

نکته کاربردی: این بخش اسکلت ذهنی درس را می‌سازد. وقتی بدانی این قطعه دقیقاً چه مسئله‌ای را حل می‌کند، بعداً موقع پروژه فقط syntax حفظی تحویل نمی‌دهی؛ تصمیم درست می‌گیری. این توضیح قرار است دید پروژه‌ای بدهد، نه اینکه فقط تعریف کتابی حفظ کنی.

suspend و delay

تابع suspend می‌تونه متوقف بشه و بعد ادامه بده. `delay` مثل `sleep` نیست که نخ رو بخوابونه؛ همکاری می‌کنه و منابع رو الکی گروگان نمی‌گیره.

مثال را با چند مقدار دیگر هم امتحان کن؛ چون برنامه‌نویسی با نگاه کردن یاد گرفته نمی‌شود، با خراب کردن و درست کردن جا می‌افتد.

suspend
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

suspend fun fetchUser(): String {
    delay(1000)
    return "کاربر آماده است"
}

fun main() = runBlocking {
    println(fetchUser())
}
خروجی یا نتیجه اجرا
بعد از حدود یک ثانیه:
کاربر آماده است
نکته کاربردی: اینجا باید به رفتار کد نگاه کنی، نه فقط ظاهرش. مقدارها را عوض کن و دوباره اجرا بگیر تا دستت بفهمد برنامه چطور واکنش نشان می‌دهد. اگر خروجی با چیزی که انتظار داشتی فرق کرد، اول مقدار متغیرها و مسیر اجرا را چک کن؛ ۹۰٪ باگ‌های اول راه همین‌جا قایم شده‌اند.

launch برای اجرای همزمان

با `launch` کاری رو شروع می‌کنی که نتیجه مستقیم برنمی‌گردونه. مثل اینکه یکی رو بفرستی چایی بیاره، خودت هم ادامه بدی.

مثال را با چند مقدار دیگر هم امتحان کن؛ چون برنامه‌نویسی با نگاه کردن یاد گرفته نمی‌شود، با خراب کردن و درست کردن جا می‌افتد.

launch
import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(500)
        println("کار پس‌زمینه تموم شد")
    }

    println("برنامه هنوز زنده‌ست")
}
خروجی یا نتیجه اجرا
برنامه هنوز زنده‌ست
کار پس‌زمینه تموم شد
نکته کاربردی: این الگو در پروژه واقعی زیاد تکرار می‌شود. از همین حالا تمیز و خوانا بنویس تا بعداً موقع دیباگ، خودت به خودت فحش ندهی. اگر خروجی با چیزی که انتظار داشتی فرق کرد، اول مقدار متغیرها و مسیر اجرا را چک کن؛ ۹۰٪ باگ‌های اول راه همین‌جا قایم شده‌اند.

async و await

اگر نتیجه می‌خوای، `async` بزن و با `await` بگیر. برای چند درخواست همزمان عالیه؛ البته بی‌حساب همزمانی نساز که سرور بدبخت نفسش بگیره.

مثال را با چند مقدار دیگر هم امتحان کن؛ چون برنامه‌نویسی با نگاه کردن یاد گرفته نمی‌شود، با خراب کردن و درست کردن جا می‌افتد.

async/await
import kotlinx.coroutines.*

fun main() = runBlocking {
    val a = async { 10 }
    val b = async { 20 }

    println(a.await() + b.await())
}
خروجی یا نتیجه اجرا
30
نکته کاربردی: نکته حرفه‌ای این است که Kotlin معمولاً راه کوتاه‌تر دارد، ولی کوتاه‌تر همیشه بهتر نیست. اول خوانایی، بعد زرنگ‌بازی. اگر خروجی با چیزی که انتظار داشتی فرق کرد، اول مقدار متغیرها و مسیر اجرا را چک کن؛ ۹۰٪ باگ‌های اول راه همین‌جا قایم شده‌اند.

نتیجه این درس 🎯

Coroutine یعنی کارهای زمان‌بر را بدون قفل کردن برنامه مدیریت کنی. برای شبکه، دیتابیس، فایل و UI مدرن، این یکی از ستون‌های اصلی Kotlin است.

تمرین! 🧠

دو coroutine بساز که یکی بعد از ۵۰۰ms و یکی بعد از ۱۰۰۰ms پیام چاپ کند.

جواب تمرین
import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(500)
        println("اولی رسید")
    }

    launch {
        delay(1000)
        println("دومی هم با تاخیر شاهانه رسید")
    }
}
خروجی جواب
اولی رسید
دومی هم با تاخیر شاهانه رسید