---
title: 'Eş Yordamlar'
source: 'https://academia.sh/tr/kurslar/kotlin/es-yordamlar'
course: 'Kotlin Dili'
language: tr
updated: '2026-10-21T05:02:39+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'launch, join, async ve await ile başlatılan işlerin sırasını çıktıdan okur'
    - 'suspend ile işaretlenmiş fonksiyonun yalnız eş yordam içinden çağrılabildiğini açıklar'
    - 'İki ayrı async çağrısının Deferred nesnelerinin eşit sayılmadığını ölçer'
    - 'Deferred yerine await sonucunun ve bağlam öğesinin içerikle karşılaştırıldığını gösterir'
---

# Eş Yordamlar

Eş yordamın askıya alındığı noktada sıra verdiğinin, sonucun await ile alındığının ve iki ayrı başlatılmış işin eşit sayılmadığının ölçümü.

Önceki derste tembel dizi, bir hesabın tarifini taşıyordu ve hesap uç işlem gelince çalışıyordu. Bazı hesaplar ise başka bir işin bitmesini bekler: bir dosyanın okunması, bir ağ yanıtı, bir zamanlayıcı. Bu bekleme sırasında çağıranı tutmak istenmez. Kotlin bu durum için **eş yordam (coroutine)** sunar: bir iş yarıda durabilir, kontrolü bırakabilir ve sonra kaldığı yerden devam edebilir.

Eş yordam kullanan kod `kotlinx.coroutines` eş yordam kitaplığını içe alır. Bu kitaplık dilin kendisinde değil, ayrı bir pakettedir; bu derste ve bir sonrakinde `import kotlinx.coroutines.*` satırı gerekir, önceki derslerde gerekmiyordu. Bu derste üç yapı yeter. `suspend` ile işaretlenmiş **askıya alınan fonksiyon**, gövdesinde yarıda durabilen fonksiyondur ve yalnız bir eş yordamdan ya da başka bir askıya alınan fonksiyondan çağrılır. `launch` yeni bir eş yordam başlatır ve bir `Job` döndürür; sonuç beklenmez, yalnız bitişi `join()` ile beklenir. `async` yeni bir eş yordam başlatır ve sonucu taşıyan bir `Deferred` döndürür; sonuç `await()` ile alınır. Üçünü de bir eş yordam kapsamı içinde çalıştırmak gerekir, bu derste kapsamı `runBlocking` kurar: `main` fonksiyonunu çağıran iş parçacığını, içindeki bütün eş yordamlar bitene kadar bekletir.

Eş yordam bir iş parçacığı değildir. Bir iş parçacığı işletim sisteminin sıraladığı bir yürütme birimidir; eş yordam ise kitaplığın bir **dağıtıcı (`Dispatchers`)** üzerinde yürüttüğü iştir ve aynı iş parçacığında sırayla yürüyebilir. Bu derste hiçbir ölçüm süreye ya da iş parçacığı adına dayanmaz: zamanlama çıktıdan okunmaz, yalnız işlerin sırası ve sonuçları okunur. Ölçü sorusu önceki derslerdeki gibi kalır: **bu iki değeri eşit yapan ne?**

```kotlin
// Criterion.kt — iki degeri esit yapan olcutu siniflandirir
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
    val sameObject = first === twin
    val equalTwin = first == twin
    val equalOther = first == other

    val name: String = when {
        sameObject -> "tekil"
        !equalTwin -> "kimlik"
        !equalOther -> "icerik"
        else -> "kismi-icerik"
    }

    fun report() {
        println("$label: ayni-nesne=$sameObject, ikiz=$equalTwin, farkli=$equalOther -> $name")
    }
}

fun summarize(checks: List<Criterion>) {
    for (name in listOf("tekil", "kimlik", "icerik", "kismi-icerik")) {
        println("$name: ${checks.count { it.name == name }}")
    }
}
```

## Sıra: launch, join, async, await

Aşağıdaki blok `doubled` adında bir askıya alınan fonksiyon tanımlar; içinde `yield()` çağrısı eş yordamın sırayı başkasına verebileceği bir nokta kurar. `main` üç şeyi ölçer. İlk olarak `launch` bloğunun, `launch` çağrısını izleyen satırdan önce mi sonra mı çalıştığı: ikisi de aynı listeye kayıt ekler. İkinci olarak iki `async` işinin sonuçlarının `await()` ile toplanması. Üçüncü olarak `coroutineScope` içinde başlatılan üç işin sonuçlarının sırayla alınması.

```kotlin
// Main.kt — launch, async ve await
import kotlinx.coroutines.*

suspend fun doubled(n: Int): Int {
    yield()
    return n * 2
}

fun main() = runBlocking {
    val log = mutableListOf<String>()
    val job = launch { log.add("launch") }
    log.add("main")
    job.join()
    println(log)
    val first = async { doubled(21) }
    val second = async { doubled(4) }
    println(first.await() + second.await())
    val total = coroutineScope {
        val parts = listOf(1, 2, 3).map { async { doubled(it) } }
        parts.map { it.await() }
    }
    println(total)
}
```

```
[main, launch]
50
[2, 4, 6]
```

İlk satır önemli bir sonucu gösterir: `launch` bloğu, `launch` çağrısı döner dönmez çalışmadı. Önce `main` kaydı eklendi, `launch` kaydı ondan sonra geldi. `runBlocking` tek bir iş parçacığında çalıştığı için yeni eş yordam, başlatan eş yordam bir askıya alma noktasına gelene kadar sırada bekler; burada bu nokta `job.join()` çağrısıdır. Sıranın böyle çıkması dağıtıcıya bağlıdır; bu yüzden ders bu sırayı değil, `join()` çağrısından sonra her iki kaydın da listede olduğunu güvence sayar.

İkinci satırda iki `async` işi `21 * 2` ve `4 * 2` sonuçlarını üretir; `await()` bunları alır ve toplam `50` çıkar. Üçüncü satırda `coroutineScope`, içinde başlatılan bütün işler bitmeden dönmez; üç `async`'in sonuçları `await()` ile listede başlatılış sırasında alınır: `[2, 4, 6]`. Sonuçların sırası başlatma sırasını izler, çünkü `parts` listesi `await()` çağrılarını o sırayla yapar. İşlerin hangisinin önce bittiği ise bu çıktıdan okunamaz ve okunmaya çalışılmaz.

**KE20.** Bu ölçümler `runBlocking` kapsamı ve tek bir `Int` sonuç tipi için. Başka bir dağıtıcıda `launch` ile `main` kaydının sırası, askıya alma noktası olmayan bir fonksiyonda sıra ve işler arasındaki gerçek bitiş sırası ölçülmedi.

## Ölçüt: Kimlik

`async` bir sonuç değil, sonucun sözünü döndürür. Bu söz bir `Deferred` nesnesidir ve her `async` çağrısı yeni bir tane kurar. Aşağıdaki blok iki ayrı `async { 1000 }` çağrısını ve değeri farklı üçüncüsünü karşılaştırır. İkinci ölçüm sabit küçük bir değerle aynı soruyu sorar. İkisinde de bloklar, sorunun bir sonuç değil bir `Deferred` olduğunu görmek için `await()` çağırmaz.

```kotlin
// Main.kt — iki Deferred
import kotlinx.coroutines.*

fun main() = runBlocking {
    val checks = listOf(
        Criterion("async-1000", async { 1000 }, async { 1000 }, async { 2000 }),
        Criterion("async-1", async { 1 }, async { 1 }, async { 2 }),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
async-1000: ayni-nesne=false, ikiz=false, farkli=false -> kimlik
async-1: ayni-nesne=false, ikiz=false, farkli=false -> kimlik
tekil: 0
kimlik: 2
icerik: 0
kismi-icerik: 0
```

**KE21.** Bu ölçümler `async` ile kurulan `Deferred` nesneleri için. `launch` ile kurulan `Job` nesnelerinin ve `CompletableDeferred` gibi doğrudan kurulan sözlerin eşitliği ölçülmedi.

## Sınırlayıcı Ölçüm

Sonucu almak eşitliği değiştirir. Aşağıdaki blok üç şeyi yan yana ölçer: `Deferred` nesnelerinin kendisini, onların `await()` ile alınmış sonuçlarını ve aynı eş yordamın bağlamına konabilen iki `CoroutineName("a")` öğesini. Sonuçlar için `1000` seçildi; küçük tam sayılar kutulama sırasında paylaşılabilir ve `tekil` çıkabilir, `1000` için bu ayrıntıya bağlı kalınmaz, sonuç sandbox'ın bastığı olur.

```kotlin
// Main.kt — sonuc ve baglam ogesi
import kotlinx.coroutines.*

fun main() = runBlocking {
    val a = async { 1000 }
    val b = async { 1000 }
    val c = async { 2000 }
    val checks = listOf(
        Criterion("deferred", a, b, c),
        Criterion("sonuc", a.await(), b.await(), c.await()),
        Criterion("ad", CoroutineName("a"), CoroutineName("a"), CoroutineName("b")),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
deferred: ayni-nesne=false, ikiz=false, farkli=false -> kimlik
sonuc: ayni-nesne=false, ikiz=true, farkli=false -> icerik
ad: ayni-nesne=false, ikiz=true, farkli=false -> icerik
tekil: 0
kimlik: 1
icerik: 2
kismi-icerik: 0
```

Sınırlayıcı ölçüm şudur: **eşitliği iş değil, işin sonucunu taşıyan tip belirler.** `Deferred` kimlikle karşılaştırılır; `await()` ile alınan `Int` ise içerikle. Aynı `a` ve `b` işleri, bir kez nesne olarak, bir kez sonuç olarak ölçüldü ve ölçüt `kimlik`ten `icerik`e döndü. `CoroutineName` de içerikle karşılaştırılır: aynı adı taşıyan iki öğe eşittir, farklı adlı olan eşit değildir. Bu, eş yordam bağlamındaki bir öğenin kimlik değil değer taşıdığını gösterir.

Pratik çıkarım: iki işi karşılaştırmak gerekiyorsa `Deferred`'ları değil sonuçları karşılaştırmak gerekir; bunun için önce `await()` çağırmak gerekir. Bu çağrı da yalnız bir eş yordam içinde yapılabilir; `await()` bir askıya alınan fonksiyondur.

**KE22.** Bu ölçümler `Int` sonuçları ve `CoroutineName` öğeleri için. Başka sonuç tiplerinin eşitliği, `Dispatchers` öğelerinin eşitliği ve birden çok bağlam öğesinin birleşiminin eşitliği ölçülmedi.

## Tembel Başlatma ve İptal

`async` ve `launch` varsayılan olarak hemen sıraya girer. `start = CoroutineStart.LAZY` verilirse eş yordam kurulur ama başlatılmaz: gövdesi, `await()`, `join()` ya da `start()` çağrılana kadar hiç çalışmaz. Bu, önceki dersteki tembel dizinin fikrine benzer, ama burada tembel olan bir hesabın sonucu değil, bir işin başlangıcıdır. İptal ise başlamamış bir işi hiç çalışmadan sonlandırabilir. Aşağıdaki blok önce tembel bir `async` kurar ve `await()` öncesinde, sonrasında ona ve listeye bakar. Sonra tembel bir `launch` kurar, iptal eder ve ancak ondan sonra bekler. Sıralar yalnız `await()` ve `join()` çağrılarının güvence verdiği noktalardan okunur.

```kotlin
// Main.kt — tembel baslatma ve iptal
import kotlinx.coroutines.*

fun main() = runBlocking {
    val log = mutableListOf<String>()
    val lazy = async(start = CoroutineStart.LAZY) { log.add("govde"); 7 }
    println(log.size)
    println(lazy.isActive)
    println(lazy.await())
    println(log)
    println(lazy.isCompleted)
    val cancelled = launch(start = CoroutineStart.LAZY) { log.add("kosmaz") }
    cancelled.cancel()
    cancelled.join()
    println(cancelled.isCancelled)
    println(log)
}
```

```
0
false
7
[govde]
true
true
[govde]
```

Tembel `async` kurulduğunda liste boştu ve iş etkin değildi: gövde `await()` çağrısına kadar hiç çalışmadı. `await()` gövdeyi başlattı, sonucu `7` olarak verdi ve listeye `govde` kaydı düştü. Ardından iş tamamlanmış sayıldı. Tembel `launch` ise iptal edildiği için gövdesine hiç girmedi: son satırda `kosmaz` kaydı yoktur, ama iş `isCancelled` olarak işaretlendi. Sonuç şudur: bir eş yordamın başlatılmış olması ile bitirilmiş olması farklı şeylerdir ve `Job` bu durumu sorgulatır. Sonucu hiç kullanılmayacak bir işi iptal etmek, onun gövdesinin yan etkilerini de engelleyebilir.

**KE23.** Bu ölçümler `CoroutineStart.LAZY` ile kurulmuş, `runBlocking` kapsamındaki işler için. Çalışmakta olan bir işin ortasında iptalin etkisi ve `start()` ile başlatmanın sırası ölçülmedi.

## Bütün Çocukları Beklemek

Birden çok `launch` başlatıldığında hepsinin bitişini ayrı ayrı beklemek zorunlu değildir. `coroutineScope { }` bloğu, içinde başlatılan bütün işler bitmeden dönmez; `joinAll()` ise verilen `Job` listesindeki hepsini bekler. İki yolun da güvencesi aynıdır: dönüşten sonra işlerin yan etkileri görünürdür. Ama işlerin kendi arasındaki sırası bu güvencenin parçası değildir. Bu yüzden aşağıdaki blok sıralamayı değil, sonucu ölçer: dönüşten sonra listeyi sıralar ve kaç eleman bulunduğunu sayar.

```kotlin
// Main.kt — coroutineScope ve joinAll
import kotlinx.coroutines.*

fun main() = runBlocking {
    val log = mutableListOf<Int>()
    coroutineScope {
        for (i in 1..3) {
            launch {
                yield()
                log.add(i)
            }
        }
    }
    println(log.sorted())
    val jobs = List(4) { i ->
        launch {
            yield()
            log.add(i * 10)
        }
    }
    jobs.joinAll()
    println(log.size)
    println(jobs.all { it.isCompleted })
}
```

```
[1, 2, 3]
7
true
```

`coroutineScope` bloğu döndüğünde üç kayıt da listedeydi. Ölçüm sıralanmış listeyi bastı, çünkü işlerin hangi sırayla bittiği güvenceli değildir. İkinci kümede dört iş `joinAll()` ile beklendi ve listede toplam yedi eleman oldu; hepsi tamamlanmış göründü. Güvence ve sıra ayrımı pratikte önemlidir: bir sonucu beklemek için kapsam ya da `join` kullanılır, bitiş sırasına dayanan bir mantık ise yanlış kurulmuş olur.

**KE24.** Bu ölçümler `launch` işleri ve `Int` listesi için. Bir çocuk işin istisna fırlatması durumunda kapsamın ve kardeş işlerin davranışı bu derste ölçülmedi.

## Bağlam ve Ad

Her eş yordam bir bağlam taşır ve bağlam öğeleri anahtarla okunur. `coroutineContext[CoroutineName]` varsa adı verir, yoksa `null` döner. `withContext(CoroutineName("worker")) { }` bloğu içinde bağlam yeni adı taşır ve blok bitince eski bağlam geri gelir. `async` ya da `launch` çağrısına bir bağlam öğesi verilirse, çocuk iş onu taşır. Aşağıdaki blok adı okuyan küçük bir askıya alınan fonksiyon tanımlar ve onu dört yerden çağırır. Ad bir iş parçacığı adı değildir; yalnız bağlama konmuş bir etikettir, bu yüzden ölçülebilir.

```kotlin
// Main.kt — baglamdan ad okumak
import kotlinx.coroutines.*
import kotlin.coroutines.coroutineContext

suspend fun currentName(): String? = coroutineContext[CoroutineName]?.name

fun main() = runBlocking {
    println(currentName())
    println(withContext(CoroutineName("worker")) { currentName() })
    println(currentName())
    val named = async(CoroutineName("child")) { currentName() }
    println(named.await())
}
```

```
null
worker
null
child
```

İlk çağrıda `runBlocking` bağlamında ad yoktu ve sonuç `null`. `withContext` bloğunda ad `worker` oldu. Blok bittikten sonra ad yine `null`: bağlam değişikliği bloğa özeldi. Son satırda çocuk `async` kendi adı olan `child` değerini okudu. Bağlam bu yüzden eş yordamlar arasında bilgi taşımanın temiz bir yoludur: parametre geçirmeden, ama kapsamı belli bir biçimde.

**KE25.** Bu ölçümler `CoroutineName` öğesi ve `runBlocking` kapsamı için. `Job` öğesinin ve `Dispatchers` öğesinin bağlamdan okunması, çocuğun üst bağlamdan öğe devralışı ve iki öğenin `+` ile birleşimi ölçülmedi.

## İş Durumları ve Tekrarlı await

Her `launch` ve `async` bir `Job` taşır ve `Job` işin hangi aşamada olduğunu üç özellikle söyler: `isActive` işin sürdüğünü, `isCompleted` bittiğini, `isCancelled` iptal edildiğini gösterir. Bir işin bittiğini `join()` ya da `await()` döndükten sonra söylemek güvencelidir; başlatma ile ilk askıya alma noktası arasındaki ara durumlar ise dağıtıcıya bağlıdır ve bu derste ölçülmez. `await()` ayrıca birden çok kez çağrılabilir: sonuç bir kez hesaplanır ve sonraki çağrılar aynı değeri verir. Aşağıdaki blok önce `join()` sonrasındaki durumu, sonra aynı `Deferred` üzerinde iki `await()` çağrısını ve gövdenin kaç kez koştuğunu ölçer.

```kotlin
// Main.kt — is durumlari ve tekrarli await
import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch { yield() }
    job.join()
    println(job.isActive)
    println(job.isCompleted)
    println(job.isCancelled)
    var runs = 0
    val deferred = async { runs++; "sonuc" }
    println(deferred.await())
    println(deferred.await())
    println(runs)
}
```

```
false
true
false
sonuc
sonuc
1
```

`join()` döndükten sonra iş artık etkin değildi, tamamlanmıştı ve iptal edilmemişti. `Deferred` iki kez beklendi ve iki seferde de aynı değeri verdi; sayaç ise bir kez arttı, yani gövde yalnız bir kez koştu. `await()` işi yeniden başlatmaz, sonucu okur. Bunun bir anlamı da şudur: `Deferred` sonucu bir kez hesaplandıktan sonra saklar ve her `await()` o saklanan değeri okur. Bu, akıştaki durumun tersidir: soğuk akış her toplamada yeniden çalışır, `Deferred` ise tek hesabın sonucunu paylaşır.

**KE26.** Bu ölçümler `join()` ve `await()` sonrası durumlar için. Başlatma ile ilk askıya alma arasındaki `isActive` değeri ve hatayla biten işin durumu ölçülmedi.

## Özet

- `suspend` ile işaretlenen fonksiyon yalnız bir eş yordamdan ya da başka bir askıya alınan fonksiyondan çağrılır; `launch` sonuçsuz iş, `async` sonuçlu iş başlatır.
- `launch` çağrısı hemen çalışmaz; `runBlocking` içinde başlatan eş yordam askıya alma noktasına gelene kadar bekler. `join()` ve `await()` işin bittiğini güvence eder.
- İki ayrı `async` çağrısının `Deferred` nesneleri eşit değildir. Bu dersin ölçütü: **kimlik**.
- Sınırlayıcı ölçüm: aynı işlerin `await()` sonuçları `icerik` ölçütünü verir; `CoroutineName` öğeleri de içerikle karşılaştırılır.

## Sonraki Adım

Bu ders tek bir değer üreten işleri ele aldı. Bir iş tek değer yerine zamanla değer akıtabilir. Bir sonraki ders bu durumu akışlarla (flow) kurar ve eşitlik sorusunu orada da sorar: aynı değerleri akıtan iki akış eşit midir ve art arda gelen eşit değerler akışta nasıl ele alınır?
