---
title: '`async`/`await`'
source: 'https://academia.sh/tr/kurslar/rust-eszamanlilik/async-await'
course: 'Eşzamanlılık ve Asenkron Rust'
language: tr
updated: '2026-09-13T05:09:27+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir "await" ifadesinin bir geleceği yoklamaktan başka bir şey yapmadığını ve dönüş noktasını kaynakta işaretlediğini gösterir'
    - 'Bir kez duraklayan ve hemen hazır olan iki gövdenin yoklama sayılarını karşılaştırarak duraklama noktasının koşuma etkisini ölçer'
    - "İçteki bir geleceği bekleyen dış bir gövdenin başkası bitince sınıfına girdiğini, Go'nun Eşzamanlılık kursunun ölçtüğü bitiş sorusundan farkını göstererek açıklar"
---

# `async`/`await`

Duraklama noktaları kaynakta görünür yazılır: bir kez duraklayan bir gövde iki yoklamada, hemen hazır olan bir gövde bir yoklamada tamamlanır; içteki bir geleceği bekleyen dış bir gövde ise başkası bitince ilerler.

Önceki ders `HemenHazir` tipini kurdu: `poll` çağrıldığı anda biten, hiçbir zaman `Pending`
dönmeyen bir gelecek. Bu, mümkün en yalın gövdeydi — durmadan geçen bir gövde. Bu ders aynı
sözleşmeyi bir kez duran bir gövdeyle genişletiyor ve Rust'ın bu gövdeleri elle yazmak yerine
üretmesini sağlayan yazımı tanıtıyor: `async` ve `await`. Sorulan soru aynı kalıyor — bu iş
ilerlemek için neye ihtiyaç duyuyor — ama şimdi yanıt kaynakta görünür bir işaretle veriliyor.

Önceki dersteki `HemenHazir` ve bir sonraki bölümdeki `BirKezDuraklar`, `Future` trait'ini
elle gerçekleştiren tiplerdi: `poll` yöntemini kendi elleriyle yazdılar. Gerçek programlarda
bir eşzamansız gövdenin çoğu satırı elle yazılmıyor — `async` anahtar sözcüğüyle işaretlenmiş
sıradan bir fonksiyon gövdesi olarak yazılıyor ve derleyici, bu gövdeyi otomatik olarak bir
`Future` gerçekleştirimine çeviriyor. `async` ve `await`, elle yazılan `poll` mantığının
kaynak düzeyindeki kısaltması; bu ders o kısaltmanın altında hâlâ aynı sözleşmenin —
`Ready`/`Pending` ikilisinin — çalıştığını gösteriyor.

## Duraklama Noktası Kaynakta Yazılıdır

Önceki dersteki `HemenHazir`, `Future` trait'ini elle gerçekleştiriyordu. `BirKezDuraklar`
aynı trait'i, bir kez `Pending` dönecek biçimde gerçekleştiriyor: ilk çağrıda durumunu
değiştirip kendini bir sonraki `poll` için uyandırıyor, ikinci çağrıda tamamlanıyor.

```rust
// ilerle.rs — cekirdegin kesiti: yurutucu, bir kez duraklayan gelecek, hemen hazir gelecek,
// ve icteki gelecegi bekleyen bir eszamansiz fonksiyon.

use std::future::Future;
use std::pin::Pin;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Condvar, Mutex};
use std::task::{Context, Poll, Wake, Waker};

pub struct Uyandirici {
    pub hazir: Mutex<bool>,
    pub kosul: Condvar,
}

impl Wake for Uyandirici {
    fn wake(self: Arc<Self>) {
        *self.hazir.lock().unwrap() = true;
        self.kosul.notify_one();
    }
}

pub fn kostur<F: Future>(gorev: F) -> (F::Output, u32) {
    let mut gorev = Box::pin(gorev);
    let u = Arc::new(Uyandirici {
        hazir: Mutex::new(false),
        kosul: Condvar::new(),
    });
    let waker: Waker = u.clone().into();
    let mut baglam = Context::from_waker(&waker);
    let mut yoklama = 0;
    loop {
        yoklama += 1;
        match gorev.as_mut().poll(&mut baglam) {
            Poll::Ready(x) => return (x, yoklama),
            Poll::Pending => {
                let mut h = u.hazir.lock().unwrap();
                while !*h {
                    h = u.kosul.wait(h).unwrap();
                }
                *h = false;
            }
        }
    }
}

pub struct BirKezDuraklar {
    pub duraklandi: bool,
    pub ad: &'static str,
    pub kanal: Sender<String>,
}

impl Future for BirKezDuraklar {
    type Output = &'static str;
    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<&'static str> {
        if self.duraklandi {
            self.kanal.send(self.ad.to_string()).unwrap();
            Poll::Ready(self.ad)
        } else {
            self.duraklandi = true;
            cx.waker().wake_by_ref();
            Poll::Pending
        }
    }
}

pub struct HemenHazir {
    pub ad: &'static str,
    pub kanal: Sender<String>,
}

impl Future for HemenHazir {
    type Output = &'static str;
    fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<&'static str> {
        self.kanal.send(self.ad.to_string()).unwrap();
        Poll::Ready(self.ad)
    }
}

pub async fn disariyi_bekle(ic: BirKezDuraklar, kanal: Sender<String>) -> &'static str {
    let _ = ic.await;
    kanal.send(String::from("gorev-await-eden")).unwrap();
    "await-eden"
}
```

`disariyi_bekle` bir `async fn`. Bu yazım, gövdesini elle bir `Future` gerçekleştirimine
çevirmekten kurtarıyor: derleyici, gövdeyi kendi kendine bir durum makinesine dönüştürüyor ve
bu makine `Future` trait'ini kendisi gerçekleştiriyor. Gövde içindeki `ic.await` satırı, bu
dönüşümün gözle görülür tek izi — **duraklama noktası**. Derleyici bu satırı gördüğünde,
durum makinesine "burada duraklayabilirim" diyen bir dal ekliyor: `ic`nin `poll`u `Pending`
dönerse, dış gövde de kendi `poll`unu `Pending` ile bitiriyor ve bir sonraki çağrıda kaldığı
yerden devam ediyor. `.await` bir makro çağrısı değil, bir işleç; kod bloğu dışında da adı
serbestçe geçebilir.

Üretilen durum makinesi kavram olarak, Rust Temelleri kursunda kurulan numaralandırmaya
yakın: sonlu sayıda ayrı durumdan biri her seferinde etkin oluyor, hangi durumun etkin olduğu
bir önceki `poll` çağrısının nerede kaldığına bağlı. Fark, bu numaralandırmanın elle
yazılmamış olması — derleyici, gövdedeki her `.await` sayısı kadar duraklama durumu üretiyor
ve aralarındaki geçişi kendisi kuruyor. `disariyi_bekle`de tek bir `.await` var; bu yüzden
derleyicinin ürettiği durum makinesi yalnız iki durum arasında geçiş yapıyor: `ic`yi
bekleyen durum ve tamamlanmış durum.

Fonksiyonun imzası — `async fn disariyi_bekle(...) -> &'static str` — kaynakta yazılan dönüş
tipi `&'static str` gibi görünse de, gerçek dönüş değeri derleyicinin ürettiği bu durum
makinesi: `Future<Output = &'static str>` sözleşmesini taşıyan, adı kaynakta hiç geçmeyen bir
tip. `kostur<F: Future>` imzasındaki `F::Output` ilişkili tipi tam olarak bunu karşılıyor —
`kostur`, kendisine hangi somut durum makinesinin verildiğini bilmeden, yalnız `Future`
sözleşmesine güvenerek çalışıyor. `disariyi_bekle(ic, gonder.clone())` çağrısının kendisi de
hiçbir şeyi çalıştırmıyor; önceki derste kurulan tembellik kuralı burada da geçerli — çağrı
yalnız durum makinesinin ilk durumunu kuruyor, `kostur`a verilene kadar hiçbir alanı
işlemiyor.

## Duraklama Sayısı Koşuma Yansıyor

Aşağıdaki program üç gövdeyi aynı yürütücüyle koşturuyor: bir kez duraklayan `BirKezDuraklar`,
hiç duraklamayan `HemenHazir` ve içinde bir `BirKezDuraklar`i bekleyen `disariyi_bekle`.

```rust
// yoklama-sayilari.rs — bir kez duraklayan, hemen hazir olan ve icini bekleyen govdelerin
// yoklama sayilari.
mod ilerle;
use ilerle::{disariyi_bekle, kostur, BirKezDuraklar, HemenHazir};
use std::sync::mpsc::channel;

fn main() {
    let (gonder, al) = channel::<String>();

    let (deger_a, yoklama_a) = kostur(BirKezDuraklar {
        duraklandi: false,
        ad: "gorev-yoklanan-a",
        kanal: gonder.clone(),
    });
    let (deger_b, yoklama_b) = kostur(HemenHazir {
        ad: "gorev-hazir-b",
        kanal: gonder.clone(),
    });

    let ic = BirKezDuraklar {
        duraklandi: false,
        ad: "gorev-ic",
        kanal: gonder.clone(),
    };
    let (deger_c, yoklama_c) = kostur(disariyi_bekle(ic, gonder.clone()));

    drop(gonder);
    let mut kayitlar: Vec<String> = al.try_iter().collect();
    kayitlar.sort();

    println!("degerler: a={} b={} c={}", deger_a, deger_b, deger_c);
    println!("yoklama sayilari: a={} b={} c={}", yoklama_a, yoklama_b, yoklama_c);
    println!();
    println!("kayitlar ({}):", kayitlar.len());
    for k in &kayitlar {
        println!("  {}", k);
    }
}
```

```
degerler: a=gorev-yoklanan-a b=gorev-hazir-b c=await-eden
yoklama sayilari: a=2 b=1 c=2

kayitlar (4):
  gorev-await-eden
  gorev-hazir-b
  gorev-ic
  gorev-yoklanan-a
```

**AS2.** `gorev-yoklanan-a` iki yoklamada bitti: ilk çağrı `duraklandi`yi `true` yapıp
`Pending` döndü, ikinci çağrı `kanal.send` çağırıp `Ready` döndü. `gorev-hazir-b` tek
yoklamada bitti çünkü hiç duraklamadı. Bu iki sayı, aynı yürütücüyle koşturulan iki farklı
gövdenin **kaç kez** yoklandığının kaynaktaki davranışlarından doğrudan çıkarılabilir olduğunu
gösteriyor — yoklama sayısı burada ortama bağlı bir yan bilgi değil, gövdenin kendi
mantığından gelen belirlenimci bir sonuç. Beş ayrı koşumda üç sayı da birebir aynı kaldı.

`disariyi_bekle`nin yoklama sayısı da ikiydi, ama nedeni farklı. Bu gövde `ic.await`e
ulaştığında, kendi `poll`u içeriden `ic`nin `poll`unu çağırıyor. İlk dış çağrıda `ic`
`Pending` döndürüyor, dış gövde de bunu olduğu gibi yukarı taşıyıp `Pending` dönüyor. İkinci
dış çağrıda `ic` tamamlanıyor (`gorev-ic` kaydı burada yazılıyor), `.await` satırı bitiyor,
kalan gövde (`gorev-await-eden` kaydı) aynı çağrı içinde çalışıp `Ready` dönüyor. İki yoklama
da, ama biri kendi durumundan, öbürü içindeki başka bir geleceğin durumundan geliyor.

## Duraklama Noktası Sayısı Yoklama Sayısını Belirler

Bir gövde tek bir `.await` ile sınırlı değil; art arda birden fazla duraklama noktası
taşıyabilir. Aşağıdaki kesit `disariyi_bekle`nin yerine, iki ayrı `BirKezDuraklar`ı sırayla
bekleyen bir gövde tanımlıyor. `HemenHazir` ve `disariyi_bekle` bu ölçümde gerekmediği için
kesit dışında bırakılıyor.

```rust
// ilerle.rs — cekirdegin kesiti degisti: iki ardisik duraklama noktasi tasiyan bir govde
// ekleniyor, HemenHazir ve disariyi_bekle bu olcumde gerekmiyor.

use std::future::Future;
use std::pin::Pin;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Condvar, Mutex};
use std::task::{Context, Poll, Wake, Waker};

pub struct Uyandirici {
    pub hazir: Mutex<bool>,
    pub kosul: Condvar,
}

impl Wake for Uyandirici {
    fn wake(self: Arc<Self>) {
        *self.hazir.lock().unwrap() = true;
        self.kosul.notify_one();
    }
}

pub fn kostur<F: Future>(gorev: F) -> (F::Output, u32) {
    let mut gorev = Box::pin(gorev);
    let u = Arc::new(Uyandirici {
        hazir: Mutex::new(false),
        kosul: Condvar::new(),
    });
    let waker: Waker = u.clone().into();
    let mut baglam = Context::from_waker(&waker);
    let mut yoklama = 0;
    loop {
        yoklama += 1;
        match gorev.as_mut().poll(&mut baglam) {
            Poll::Ready(x) => return (x, yoklama),
            Poll::Pending => {
                let mut h = u.hazir.lock().unwrap();
                while !*h {
                    h = u.kosul.wait(h).unwrap();
                }
                *h = false;
            }
        }
    }
}

pub struct BirKezDuraklar {
    pub duraklandi: bool,
    pub ad: &'static str,
    pub kanal: Sender<String>,
}

impl Future for BirKezDuraklar {
    type Output = &'static str;
    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<&'static str> {
        if self.duraklandi {
            self.kanal.send(self.ad.to_string()).unwrap();
            Poll::Ready(self.ad)
        } else {
            self.duraklandi = true;
            cx.waker().wake_by_ref();
            Poll::Pending
        }
    }
}

pub async fn iki_kez_bekle(ic1: BirKezDuraklar, ic2: BirKezDuraklar) -> &'static str {
    let _ = ic1.await;
    let _ = ic2.await;
    "iki-kez-bekleyen"
}
```

```rust
// iki-duraklama.rs — iki ardisik duraklama noktasi tasiyan bir govdenin yoklama sayisi.
mod ilerle;
use ilerle::{iki_kez_bekle, kostur, BirKezDuraklar};
use std::sync::mpsc::channel;

fn main() {
    let (gonder, al) = channel::<String>();

    let ic1 = BirKezDuraklar {
        duraklandi: false,
        ad: "gorev-ic-birinci",
        kanal: gonder.clone(),
    };
    let ic2 = BirKezDuraklar {
        duraklandi: false,
        ad: "gorev-ic-ikinci",
        kanal: gonder.clone(),
    };
    let (deger, yoklama) = kostur(iki_kez_bekle(ic1, ic2));

    drop(gonder);
    let mut kayitlar: Vec<String> = al.try_iter().collect();
    kayitlar.sort();

    println!("deger: {}", deger);
    println!("yoklama sayisi: {}", yoklama);
    println!("kayitlar ({}):", kayitlar.len());
    for k in &kayitlar {
        println!("  {}", k);
    }
}
```

```
deger: iki-kez-bekleyen
yoklama sayisi: 3
kayitlar (2):
  gorev-ic-birinci
  gorev-ic-ikinci
```

**AS3.** İki duraklama noktası üç yoklamaya karşılık geliyor: ilk çağrı `ic1`i `Pending`
yapıyor, ikinci çağrı `ic1`i bitirip `ic2`nin ilk `poll`unu tetikliyor (o da `Pending`
dönüyor, aynı dış çağrı içinde), üçüncü çağrı `ic2`yi bitirip gövdenin kalanını çalıştırıyor.
Genel kural buradan çıkıyor: art arda gelen $n$ duraklama noktası, art arda gelen $n+1$
yoklama gerektiriyor — son çağrı her zaman ilerlemeyi tamamlayan, `Pending` döndürmeyen
çağrı. Bu sayı da beş ayrı koşumda birebir aynı kaldı; kaynaktaki `.await` sayısı, koşumdaki
yoklama sayısını önceden belirliyor. `gorev-ic-ikinci` kaydı hiçbir koşumda
`gorev-ic-birinci` kaydı oluşmadan önce yazılamaz — ikinci duraklama noktası, `ic2`nin ilk
`poll`u ancak `ic1` tamamlandıktan sonra çağrıldığı için, birincisi bitmeden hiç
değerlendirilmiyor. Bu sıra, defterde alfabetik olarak sıralandığı için değil, durum
makinesinin dallarını yalnız bir öncekini tamamladıktan sonra geçebildiği için garanti
altında.

## Beklemek ile Yoklanmak Aynı Şey Değil

`gorev-ic` — `disariyi_bekle`nin içindeki `BirKezDuraklar` örneği — **yoklanınca** sınıfına
giriyor: `gorev-yoklanan-a` ile aynı tip, aynı davranış, `.await` içinde kullanılmış olması
sınıfını değiştirmiyor. Buna karşılık `gorev-await-eden` kaydı — `disariyi_bekle`nin kendi
tamamlanma satırı — **başkası bitince** sınıfına giriyor. Fark şu: `gorev-ic` doğrudan
yoklanıyor ve kendi ilerlemesi için yalnız tekrar tekrar çağrılmaya ihtiyaç duyuyor;
`disariyi_bekle` ise `ic` tamamlanmadan hiçbir zaman kendi son satırına ulaşamıyor —
ilerlemesi kendi yoklanmasına değil, **başka bir görevin bitmesine** bağlı.

Go'nun Eşzamanlılık kursu bir birimin nasıl duracağını ölçmüştü — kendi, beklediği geldiği
için, iptal edildiği için ya da hiç durmadan. Bu ders aynı soruyu sormuyor; burada ölçülen
bitiş değil, **duraklayıp nereden devam ettiği**. `disariyi_bekle` hiçbir zaman durmuyor,
yalnız bir kez `Pending` dönüp aynı noktadan devam ediyor — ve bu devam noktası, `.await`
yazıldığı satırla birebir eşleşiyor. Kaynaktaki tek bir satır, koşumdaki duraklama noktasını
önceden gösteriyor.

Bu ayrım, İş Parçacıkları konusundaki kanal bekleyişiyle karşılaştırıldığında daha net
görünüyor. O konuda bir iş parçacığı bir kanaldan haber beklerken işletim sistemi tarafından
askıya alınıyordu; iş parçacığının kendisi, beklediği süre boyunca hiçbir işlemci zamanı
tüketmiyordu ama bu askıya alma programcının kontrolünün dışındaydı. `disariyi_bekle` ise
kendi askıya alınmasını **kendi yazdığı** `.await` satırıyla tarif ediyor; yürütücü bu satırı
görmüyor bile, yalnız `poll`un döndürdüğü `Pending`i görüyor. Askıya alınma kararı burada
kaynağın kendisinde, iş parçacığı örneğinde ise çalışma zamanının bir kararında duruyor.

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

Duraklama noktası kaynakta yazılı, ama **ne kadar duraklayacağı yazılı değil**. `ic.await`
satırı, `disariyi_bekle`nin orada durabileceğini söylüyor; kaç `poll` çağrısı sonra devam
edeceği, `ic`nin kendi mantığına bağlı ve bu ders yalnız **sayıyı** basıyor, süreyi değil.
`BirKezDuraklar` bir kez duruyor çünkü öyle yazıldı; başka bir gerçekleştirim on kez durabilir,
kaynaktaki `.await` görünümü değişmez. Duraklama noktasının görünürlüğü, duraklamanın **var
olduğunu** kaynaktan okumayı sağlıyor; kaç kez tekrarlanacağını değil.

Bu, kursun ortama bağlı veri yasağının bir sonraki dersteki I/O örneğinde de karşımıza
çıkacak biçimi: modellenen bir bekleme, gerçek bir gecikmenin süresini değil, yalnız kaç kez
yoklandığını ölçülebilir kılıyor.

Aynı sınır, iki ardışık duraklama noktasının ölçümünde de geçerli: `n+1` kuralı yalnız
duraklama noktası **sayısını** sayıyor, her duraklamanın ne kadar sürdüğünü değil. `ic1` ile
`ic2` arasında gerçek bir programda saniyeler geçebilir ya da hiç geçmeyebilir — kural
değişmez, çünkü kural yoklama çağrılarının **kaç kez** yapıldığını, aralarında ne kadar
zaman geçtiğini değil.

## Özet

- `async fn`, bir gövdeyi elle `Future` gerçekleştirmek yerine derleyicinin otomatik ürettiği
  bir durum makinesine çevirir; `.await` bu makinedeki **duraklama noktasını** işaretler.
- Bir kez duraklayan gövde iki yoklamada, hiç duraklamayan gövde bir yoklamada tamamlanır;
  yoklama sayısı belirlenimcidir ve beş ayrı koşumda değişmez.
- İçteki bir geleceği bekleyen gövde de **yoklanınca** sınıfına girer, ama o gövdenin kendi
  tamamlanma noktası ilerlemek için **başkası bitince** sınıfına girer — ikisi aynı görev
  içinde bir arada bulunabilir.
- Go'nun Eşzamanlılık kursu bir birimin nasıl duracağını (bitiş) ölçtü; bu ders nerede
  duraklayıp nasıl devam ettiğini (duraklama ve devam) ölçüyor.
- Duraklama noktası kaynakta görünür yazılır, ama kaç kez tetikleneceği kaynaktan okunmaz —
  yalnız koşumda ölçülür. Art arda gelen $n$ duraklama noktası, art arda gelen $n+1$ yoklama
  gerektirir: son çağrı her zaman `Pending` döndürmeyen, ilerlemeyi tamamlayan çağrıdır.
- Bir `async fn`in gövdesi çağrıldığı anda çalışmaz, yalnız derleyicinin ürettiği durum
  makinesinin ilk durumunu kurar — önceki dersteki tembellik kuralı burada da geçerlidir.

## Sonraki Adım

Bu ders, bir gövdenin kaç kez duraklayıp devam ettiğini ölçtü; henüz sorulmayan soru, bu
duraklama ve devam çağrılarının **kim tarafından** zamanlandığı. `kostur` şimdiye kadar
verili bir araç olarak kullanıldı — kod her defasında aynı işlevi çağırdı ve onun uyandırılana
kadar beklediğini varsaydı. Sıradaki ders bu varsayımı sorguluyor ve `kostur`u doğrudan konu
ediniyor: aynı gövdeyi iki farklı yürütücü kurgusuyla koşturup, yürütücünün görevin gövdesine
hiçbir şey eklemediğini, yalnız **ne zaman** yoklayacağına karar verdiğini ölçüyor.
