İçeriğe geç
academia.sh

Ders 07 / 17

async/await

async fonksiyonun her zaman söz döndürmesi, await'in fonksiyonu askıya alması, sözdizimsel katmanın söz zincirine karşılığı ve farkında olmadan sıralı beklemeye düşme hatası.

İçindekiler

Sözler eşzamansız işlemi bir değere dönüştürdü, birleştiriciler de beklemeyi düzenledi. Buna karşın kodun okunuşu değişmedi: her adımın sonucu hâlâ bir geri çağrının içinde işleniyor ve akış, kaynak metinde yukarıdan aşağı akmıyor.

Bu ders, aynı söz zincirlerini sıralı bir yazımla ifade eden katmanı kurar. Vurgulanacak nokta baştan söylenmelidir: async ve await yeni bir eşzamansızlık düzeneği getirmez. İkisi de sözler üzerine kurulmuş sözdizimsel bir katmandır; olay döngüsü kuralları aynen geçerlidir.

async Her Zaman Söz Döndürür

async anahtar sözcüğüyle tanımlanan bir fonksiyon, ne döndürürse döndürsün bir söz döndürür. Sıradan bir değer döndürürse, söz o değerle yerine gelir.

async function sabitOlcum() {
  return { istasyon: "A1", deger: 21.4 };
}

const donen = sabitOlcum();
console.log("dönen değer bir söz mü:", donen instanceof Promise);
console.log("beklenmiş sonuç:", await donen);
dönen değer bir söz mü: true
beklenmiş sonuç: { istasyon: 'A1', deger: 21.4 }

Bunun tersi de doğrudur: async fonksiyonun gövdesinde atılan bir hata, dışarıya fırlamaz; dönen sözü reddeder. Hata yayılımının ayrıntısı sonraki dersin konusudur.

await Fonksiyonu Askıya Alır, İş Parçacığını Değil

await bir sözün sonuçlanmasını bekler. Beklerken yalnız içinde bulunduğu fonksiyonu askıya alır; iş parçacığı serbest kalır ve çağıran koda geri döner.

Bunun gözlenebilir sonucu şudur: async bir fonksiyonun gövdesi, ilk await ifadesine kadar eşzamanlı çalışır; oradan sonrası bir mikro göreve dönüşür.

async function olcumIsle() {
  console.log("2 — async gövde eşzamanlı başlar");
  await null;
  console.log("4 — ilk await sonrası mikro görevde sürer");
}

console.log("1 — çağrıdan önce");
olcumIsle();
console.log("3 — çağrıdan sonra");
1 — çağrıdan önce
2 — async gövde eşzamanlı başlar
3 — çağrıdan sonra
4 — ilk await sonrası mikro görevde sürer

Beklenen değer bir söz bile değildi; await null da bir mikro görev turu ekler. Kural biçimsel olarak şudur: await ifadesi, beklenen değeri Promise.resolve ile sarar ve devamını o sözün geri çağrısı olarak kaydeder.

İki async fonksiyon iç içe değil, yan yana ilerleyebilir. Her await, denetimi olay döngüsüne bırakır ve diğer fonksiyonun sıradaki adımı araya girer:

async function ilkAkis() {
  console.log("A — adım 1");
  await null;
  console.log("A — adım 2");
  await null;
  console.log("A — adım 3");
}

async function ikinciAkis() {
  console.log("B — adım 1");
  await null;
  console.log("B — adım 2");
  await null;
  console.log("B — adım 3");
}

ilkAkis();
ikinciAkis();
console.log("eşzamanlı kısım bitti");
A — adım 1
B — adım 1
eşzamanlı kısım bitti
A — adım 2
B — adım 2
A — adım 3
B — adım 3

İki akış adım adım birbirine geçti; buna karşın hiçbir an ikisi aynı anda çalışmadı. Tek iş parçacıklı model bozulmadı — yalnız bölünme noktaları await ifadeleriyle açıkça işaretlendi.

Zincirin Karşılığı

Aynı akış iki yazımla da ifade edilebilir; ürettikleri söz ve sonuç aynıdır.

const KAYNAK = {
  A1: { gecikme: 30, deger: 21.4 },
  B2: { gecikme: 10, deger: 19.8 },
  C3: { gecikme: 20, deger: 23.1 },
  D4: { gecikme: 15, hata: "sensör arızası" },
};

function olcumGetir(istasyon) {
  console.log("istek başladı:", istasyon);
  return new Promise((karar, red) => {
    const kayit = KAYNAK[istasyon];
    setTimeout(() => {
      if (kayit.hata) red(new Error(`${istasyon}: ${kayit.hata}`));
      else karar({ istasyon, deger: kayit.deger });
    }, kayit.gecikme);
  });
}

async function ortalamaAl() {
  const a = await olcumGetir("A1");
  const b = await olcumGetir("B2");
  return (a.deger + b.deger) / 2;
}

function ortalamaAlZincir() {
  return olcumGetir("A1").then((a) =>
    olcumGetir("B2").then((b) => (a.deger + b.deger) / 2),
  );
}

console.log("await yazımı:", (await ortalamaAl()).toFixed(2));
console.log("zincir yazımı:", (await ortalamaAlZincir()).toFixed(2));
istek başladı: A1
istek başladı: B2
await yazımı: 20.60
istek başladı: A1
istek başladı: B2
zincir yazımı: 20.60

İki yazımın farkı okunabilirliktedir. await yazımında ara sonuçlar sıradan yerel değişkenlerdir; ikinci adımda birinci adımın sonucuna erişmek için iç içe geçmek gerekmez. Adım sayısı arttıkça fark büyür: zincir yazımında her ek adım ya yeni bir girinti ya da elle taşınan bir ara nesne ister.

Sıralı Bekleme Tuzağı

await sıralı bir okunuş verir ve bu okunuş yanıltıcı olabilir: art arda yazılmış beklemeler gerçekten art arda yürür.

const KAYNAK = {
  A1: { gecikme: 30, deger: 21.4 },
  B2: { gecikme: 10, deger: 19.8 },
  C3: { gecikme: 20, deger: 23.1 },
  D4: { gecikme: 15, hata: "sensör arızası" },
};

function olcumGetir(istasyon) {
  console.log("istek başladı:", istasyon);
  return new Promise((karar, red) => {
    const kayit = KAYNAK[istasyon];
    setTimeout(() => {
      if (kayit.hata) red(new Error(`${istasyon}: ${kayit.hata}`));
      else karar({ istasyon, deger: kayit.deger });
    }, kayit.gecikme);
  });
}

for (const istasyon of ["A1", "B2", "C3"]) {
  const olcum = await olcumGetir(istasyon);
  console.log("geldi:", olcum.istasyon, olcum.deger);
}
istek başladı: A1
geldi: A1 21.4
istek başladı: B2
geldi: B2 19.8
istek başladı: C3
geldi: C3 23.1

Çıktıdaki sıra tanıyı verir: her istek, bir öncekinin sonucu geldikten sonra başlıyor. Üç istasyon birbirinden bağımsız olduğu halde beklemeler üst üste binmiyor; toplam süre üç gecikmenin toplamı kadar.

Düzeltme, isteklerin başlatılmasını beklemekten ayırmaktır. Sözler oluşturulduğunda iş başladığına göre, önce hepsi oluşturulur, sonra tek noktada beklenir.

const KAYNAK = {
  A1: { gecikme: 30, deger: 21.4 },
  B2: { gecikme: 10, deger: 19.8 },
  C3: { gecikme: 20, deger: 23.1 },
  D4: { gecikme: 15, hata: "sensör arızası" },
};

function olcumGetir(istasyon) {
  console.log("istek başladı:", istasyon);
  return new Promise((karar, red) => {
    const kayit = KAYNAK[istasyon];
    setTimeout(() => {
      if (kayit.hata) red(new Error(`${istasyon}: ${kayit.hata}`));
      else karar({ istasyon, deger: kayit.deger });
    }, kayit.gecikme);
  });
}

const sozler = ["A1", "B2", "C3"].map((istasyon) =>
  olcumGetir(istasyon).then((olcum) => {
    console.log("geldi:", olcum.istasyon, olcum.deger);
    return olcum;
  }),
);
const olcumler = await Promise.all(sozler);
console.log("toplam:", olcumler.reduce((t, o) => t + o.deger, 0).toFixed(1));
istek başladı: A1
istek başladı: B2
istek başladı: C3
geldi: B2 19.8
geldi: C3 23.1
geldi: A1 21.4
toplam: 64.3

Üç istek de hemen başladı; sonuçlar gecikme sırasına göre geldi. Toplam bekleme, üç gecikmenin toplamı değil en büyüğü kadar.

Ölçüt basit bir sorudur: bu adım, bir öncekinin sonucuna gerçekten muhtaç mı? Yanıt hayırsa döngü içindeki await bir hatadır. Yanıt evetse — örneğin ikinci istasyonun kimliği birinci ölçümden çıkıyorsa — sıralı bekleme doğrudur ve zorunludur.

Üst Düzey await

Modül düzeyinde, bir fonksiyonun içinde olmadan da await kullanılabilir. Bu dersteki örneklerin son satırları bunu kullanır. Modül biçimlerine bağlı ayrıntılar Modüller, Araçlar ve Ekosistem kursunun konusudur; burada bilinmesi gereken tek şey, üst düzey beklemenin modülün değerlendirilmesini geciktirdiğidir.

Özet

  • async fonksiyon her zaman söz döndürür; gövdesinde atılan hata dönen sözü reddeder.
  • await yalnız kendi fonksiyonunu askıya alır; iş parçacığı serbest kalır ve çağıran koda dönülür.
  • Gövde ilk await ifadesine kadar eşzamanlı çalışır, sonrası mikro göreve dönüşür; söz olmayan bir değeri beklemek bile bir tur ekler.
  • await yazımı ile zincir yazımı aynı sözü üretir; fark okunabilirliktedir.
  • Bağımsız işler için döngü içinde await kullanmak beklemeleri sıraya sokar; doğru kalıp, işleri başlatıp tek noktada beklemektir.

Sonraki Adım

Bu derste hata yolu bilinçli olarak dışarıda bırakıldı. Sonraki ders onu ele alır: async gövdesinde atılan hatanın nereye gittiği, try/catch ile catch yazımının ne zaman ayrıştığı, birleştiricilerin hangi durumda hata bilgisini kaybettiği ve hiç kimsenin işlemediği bir reddetmenin çalışma zamanına nasıl bildirildiği. Ölçüm akışı da orada arızalı istasyona dayanıklı hale gelecek.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat