# JavaScript'te async/await ile okunur asenkron kod

> Promise zincirlerinin yerini alan async/await sözdiziminin JavaScript'teki asenkron kodu nasıl okunabilir kıldığını örneklerle açıklıyorum.

- Yayın: 2018-04-01
- Kategori: Diller
- Etiketler: JavaScript
- Okuma süresi: 3 dk okuma
- Kaynak: https://www.muhammetsafak.com.tr/blog/javascriptte-async-await-ile-okunur-asenkron-kod/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
JavaScript'te asenkron kod yazmak uzun süre boyunca "callback hell" olarak anılan bir sorunu beraberinde getirdi. Bir işlem tamamlanınca başka bir işlem, o bitince bir diğeri — ve bunlar iç içe geçtikçe kod hem yazmak hem okumak için giderek zorlaşıyordu. [Promise'ler](/blog/javascriptte-promise-ve-asenkron-akis) bu sorunu büyük ölçüde hafifletdi; ama uzun `.then()` zincirleri de kendi karmaşıklığını getirdi. `async/await` sözdizimi ise bu problemi gerçekten çözdü.

## Önce hatırlayalım: Promise zinciri

2017 yazısında Promise'lerden söz etmiştim. Bir API'den veri çekip işleyen bir senaryoda Promise zinciri şöyle görünebiliyordu:

```javascript
fetchUser(userId)
  .then(user => fetchPosts(user.id))
  .then(posts => filterActive(posts))
  .then(activePosts => renderList(activePosts))
  .catch(err => console.error(err));
```

Bu okunabilir bir zincir. Ama hata yönetimi daha karmaşık hâle geldiğinde, bazı adımlarda hem başarı hem hata durumunu ayrı ayrı ele almanız gerektiğinde, ya da döngü içinde asenkron çağrı yapmanız gerektiğinde, `.then()` zinciri hızla dağınıklaşıyor.

## async/await nedir?

**`async/await`**, Promise tabanlı asenkron kodu senkronmuş gibi yazmanızı sağlayan bir sözdizim şekeridir. Arka planda hâlâ Promise kullanılır; sadece yazım biçimi değişir.

Bir fonksiyonu `async` ile işaretlerseniz, içinde `await` kullanabilirsiniz. `await`, bir Promise resolve edilene kadar o satırda bekler ve sonucu doğrudan döndürür.

Yukarıdaki zinciri `async/await` ile yazalım:

```javascript
async function loadUserPosts(userId) {
  const user = await fetchUser(userId);
  const posts = await fetchPosts(user.id);
  const activePosts = filterActive(posts);
  renderList(activePosts);
}
```

Fark açık: satırlar yukarıdan aşağıya akıyor. Hangi adımın hangi veriyi aldığı, hangi adımın hangisine bağımlı olduğu göz önünde.

## Hata yönetimi

`async/await` ile hata yönetimi `try/catch` bloğuyla yapılıyor. Promise zincirinin `.catch()` metoduna karşılık geliyor:

```javascript
async function loadUserPosts(userId) {
  try {
    const user = await fetchUser(userId);
    const posts = await fetchPosts(user.id);
    renderList(filterActive(posts));
  } catch (err) {
    console.error('Veri yüklenemedi:', err.message);
  }
}
```

Bu yapının bir avantajı, senkron hataları da (örneğin `filterActive` içinde bir `TypeError`) aynı `catch` bloğunun yakalaması. Promise zincirinde bunu elde etmek için ekstra dikkat gerektiriyordu.

Bir tuzak noktası şu: `async` fonksiyonu çağırırken dışarıda da `await` yoksa, hata çağrı noktasında yakalanmıyor ve sessizce yutuluyor. Özellikle event listener içinden `async` fonksiyon çağırırken bu duruma dikkat etmek gerekiyor:

```javascript
// Dikkatli olun: hata burada sessizce kaybolabilir
button.addEventListener('click', async () => {
  await doSomething(); // hata throw olsa da dışarı çıkmaz
});
```

Bunun için en azından genel bir `.catch()` veya window-level hata dinleyicisi bulundurmak iyi bir alışkanlık.

## Paralel çağrılar için Promise.all

`await` ile ardışık çağrılar kolaylaşıyor ama dikkat edilmesi gereken bir nokta var: her `await` bir önceki işlemin bitmesini bekler. İki bağımsız çağrıyı art arda `await` ile yazarsanız, ikincisi birinciyi bekliyor demektir.

```javascript
// YANLIŞ: gereksiz sıralı bekleme
const user = await fetchUser(id);
const settings = await fetchSettings(id);
```

Bu ikisi bağımsız olduğunda `Promise.all` daha uygun:

```javascript
// DOĞRU: ikisi paralel başlar
const [user, settings] = await Promise.all([
  fetchUser(id),
  fetchSettings(id)
]);
```

`Promise.all` bir dizi Promise alır, hepsini paralel olarak başlatır ve hepsi tamamlandığında sonuçları dizi olarak döndürür.

## Döngü içinde asenkron işlem

Asenkron kodun önceki yazım biçimlerinde en çok kafa karıştıran konu döngülerdi. `async/await` bunu önemli ölçüde sadeleştiriyor:

```javascript
async function processItems(items) {
  for (const item of items) {
    await processOne(item);
  }
}
```

Her öğe işlenince bir sonrakine geçiyor. Sıralı işleme istiyorsanız bu yeterli. Paralel işleme istiyorsanız `Promise.all` ile `map` kombinasyonuna başvurmak gerekiyor.

Dikkat edilmesi gereken bir nokta: `forEach` döngüsü `async/await` ile beklediğiniz gibi çalışmıyor. `Array.prototype.forEach` callback'in döndürdüğü Promise'i beklemeden devam ediyor. Döngü içinde `await` kullanmak istiyorsanız `for...of` ya da `for` döngüsü kullanın.

## Tarayıcı ve Node.js desteği

`async/await`, ES2017 (ES8) standardıyla geldi. 2018 itibarıyla modern tarayıcıların büyük çoğunluğu doğrudan destekliyor. Eski tarayıcıları desteklemek gerekiyorsa Babel ile transpile edilebilir. Node.js tarafında ise 7.6 sürümünden itibaren sorunsuz çalışıyor.

## Sonuç

`async/await`'in benim için en büyük kazanımı, asenkron kodu "okurken" beyin gücü harcamamak. Promise zincirini çözerken zihinsel olarak bir yüklenme oluyordu; `async/await` ile kod neredeyse düzyazı gibi akıyor. Bu sözdizim şekerine geçtikten sonra geri dönmek istemedim.
