البرمجة غير المتزامنة في Kotlin: دليل كامل عملي
تتطلب التطبيقات الحديثة أداءً عاليًا واستجابة سريعة. أصبحت البرمجة غير المتزامنة في Kotlin باستخدام الكوروتينات (coroutines) المعيار الفعلي للتعامل مع طلبات الشبكة وقواعد البيانات وعمليات الملفات. في هذا الدليل، سنشرح المفاهيم الأساسية، ونقدم أمثلة عملية، ونعطي قوالب جاهزة للاستخدام في مشاريعك.
1. أساسيات الكوروتينات في Kotlin
الكوروتينات هي خيوط خفيفة الوزن تسمح بإيقاف تنفيذ الدالة دون حظر الخيط الرئيسي. على عكس الخيوط التقليدية، يمكن إيقاف الكوروتينات في أي لحظة واستئنافها لاحقًا، مما يجعلها مثالية للعمليات غير المتزامنة.
1.1. إضافة التبعيات
للبدء، أضف التبعيات في ملف build.gradle.kts:
dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0") // لنظام Android}1.2. أول كوروتين
لننشئ كوروتينًا بسيطًا باستخدام runBlocking - الجسر بين العالم المتزامن وغير المتزامن:
import kotlinx.coroutines.*
fun main() = runBlocking { launch { delay(1000L) // تأخير غير حاظر println("العالم!") } println("مرحبًا،") delay(2000L) // انتظار انتهاء الكوروتين}في هذا المثال، يقوم launch بتشغيل كوروتين جديد، و delay هي دالة إيقاف (suspend) لا تحظر الخيط.
2. دوال Suspend والتوازي المنظم
2.1. إنشاء دوال Suspend
يجب وضع علامة suspend على أي دالة يمكن إيقافها. يمكن استدعاء هذه الدوال فقط من داخل الكوروتينات أو دوال suspend الأخرى:
suspend fun fetchUserData(userId: String): User { delay(2000L) // محاكاة طلب شبكة return User(userId, "إيفان بيتروف")}
suspend fun fetchUserPosts(userId: String): List<Post> { delay(1500L) return listOf(Post("العنوان 1"), Post("العنوان 2"))}2.2. التنفيذ التسلسلي
افتراضيًا، يتم تنفيذ الكوروتينات بشكل تسلسلي داخل launch واحد:
fun main() = runBlocking { val time = measureTimeMillis { val user = fetchUserData("123") val posts = fetchUserPosts("123") println("${user.name} لديه ${posts.size} منشورات") } println("وقت التنفيذ: $time مللي ثانية") // ~3500 مللي ثانية}2.3. التنفيذ المتوازي باستخدام async/await
للتنفيذ المتوازي، استخدم async و await:
fun main() = runBlocking { val time = measureTimeMillis { val user = async { fetchUserData("123") } val posts = async { fetchUserPosts("123") } println("${user.await().name} لديه ${posts.await().size} منشورات") } println("وقت التنفيذ: $time مللي ثانية") // ~2000 مللي ثانية (بالتوازي)}3. موزعات الكوروتينات والسياقات
تحدد الموزعات الخيط أو مجموعة الخيوط التي سينفذ عليها الكوروتين. الموزعات الرئيسية:
- Dispatchers.Default - للمهام كثيفة الاستخدام للمعالج (افتراضي)
- Dispatchers.IO - لعمليات الإدخال/الإخراج (الشبكة، الملفات)
- Dispatchers.Main - لخيط واجهة المستخدم (Android, Swing)
- Dispatchers.Unconfined - غير مرتبط بخيط محدد
3.1. مثال مع Dispatchers.IO
fun main() = runBlocking { launch(Dispatchers.IO) { // طلب شبكة val response = httpClient.get("https://api.example.com/data") withContext(Di