Synchronous programlamada işlemler sırayla gerçekleşir. Başka bir deyişle bir işlem bitmeden diğerine geçiş yapılmaz. Aktif olan devam ederken sıradaki işlem bloklanır.

Bildiğiniz gibi Javascript bir single-threaded programlama dilidir. Yani kodu satır satır okur ve sırayla işleme sokar. Bu nedenle yukarıda verilen örneğin outputu şu şekildedir.

Asynchronous Programlama Nedir?

Callbacks, Promise ve Async-Await

Bir örnek üzerinden bu yöntemleri, aralarındaki farkları ve kullanım kolaylıklarını inceleyelim.

Diyelimki random zamana bağlı olarak string yazdıran bir fonksiyonumuz var.
Bu fonksiyonu kullanarak A, B, C harflerini sırayla yazdıralım.

Her ne kadar sırayla çağırılmış olsalar da printAll fonksiyonu her çağırıldığında A, B, C harfeleri farklı sırayla loglanır. Daha önceden de açıkladığım gibi printString fonksiyonu asynchronous çalışır. A, B, C için her fonksiyon sırayla çalıştırılır ancak setTimeOut fonksiyonundan dolayı farklı zamanlarda gercekleşirler. Bu da A, B, C harflerinin farklı sırayla loglanmasına sebep olur.

Peki bir fonsiyonu art arda kullanmak istiyorsak ve sırayla gercekleşmesini bekliyorsak ne yapmalıyız? Bu sorunun cevabına geçmeden önce de buna neden ihtiyaç duyduğumuzu da belirtmek istiyorum.

Bu tarz işlemlere bir yere istek atarken ve o istekten dönen cevaplara göre kodun devamını getiriyorsak ihtiyaç duyabiliriz. Çünkü kodu sırayla yazsak bile istek atan fonksiyon satırı okunup isteğin cevabına göre işlemi gerçekleştirecek olan fonksiyon satırına geçildiğinde eğer istek bir cevap dönmediyse undefined hatası ile karşılaşırız. Bu hata ile karşılaşmak istemiyorsak da Promise in resolve olduğu yere istekten gelen cevap ile işlem yapan fonksiyonu çağırabiliriz. Aynı zamanda bu isteğin dönmesini beklerken çalışan kodun bulunduğu satırdan sonrası için çalışmayı durdurur. Bu tarz hataların önüne geçebilmek için de calbackler,promiseler ve async-await kullanılır.

Callbacks

Callback fonksiyonları bağımsız bir değişken olarak başka bir fonksiyona geçirilen ve ilk fonksiyonun işlemi tamamlandıktan sonra diger fonksiyonun işlevini başlatan fonksiyonlardır. Callbackler genellikle asynchronous işlemler tamamlandıktan sonra kod yürütülmesine devam etmek için kullanılır.

Gelin bir örnekle duruma göz atalım.

Şimdi de addAll fonksiyonunu kullanarak A,B, C harflerini yazdıralım.

Gördüğünüz gibi bu sefer A,B, C harflerini sırayla yazdırmayı başardık lakin kodumuz oldukça karışık gözüküyor. Zaten callbakleri nerede ve ne zaman kullanacağımıza bu nedenle özen göstermeliyiz. Çünkü fonksiyon içinde fonksiyonları kullanmaya başladıkça kodu okumak gittikçe zorlaşacak ve zamanla “Callback Hell” adı verilen problem ortaya çıkacak. Ayrıca Callbacklerin sebep olduğu bazı hatalar da vardır. Bu hatalardan bazıları şunlardır;

  • Callback’in beklenenden erken çağrılması,
  • Callback’in hiç çağrılmaması,
  • Callback’in beklenilenden az veya çok çağrılması,
  • Gerekli parametreleri doğru bir şekilde alamaması,
  • Hataların kontrolünde zayıflık

Peki bu hataları yaşamamak için ve işlevlerimi yerine getirmek için hangi yolu izlemeliyiz? Bu sorunun cevabı için gelin beraber bir de promiselere göz atalım.

Promise

Promiseler benim gibi Javascript’e yeni başlayan yazılımcılar için büyük ihtimalle en çekimser yaklaşılan konulardan biridir. Peki nedir bu promiseler?
Promiseler, ‘Callback Hell’ durumundan kaçınmak için ve beklenmeyen durumla karşılaşıldığında hata kontrolünün daha rahat yapılabilmesi için geliştirilmiş ES6 olarak bilinen ECMAScript 2015 ile bize sunulan asynchronous bir yapıdır.

Bir promise 3 durumdan oluşur;

  • Pending: Bu başlangıç aşamasıdır. Bu aşamada bir şey gerçekleşmez. Bu aşama için şöyle düşünebiliriz, müşteri sana bir sipariş verecektir. Ama henüz bir şey sipariş etmemiştir.
  • Resolved: Bu aşama ise işlemin sonuçlandığı ve başarılı olduğu aşamadır. Yani müşteri siparişini almış ve memnun kalmıştır.
  • Rejected: Bu aşama ise hata ile sonuçlanan aşamadır. İstenen sipariş gelmemiş ve müşteri restoranı terk etmiştir.

Şimdi gelin beraber addString fonksiyonumuzu promise kullanarak yazalım.

Gördüğünüz gibi callback kullanmak yerine tüm fonksiyonu bir promise içerisine koyduk. Bu promise sonucunda fonksiyonumuz ya resolve olacak ya da reject. Ayrıca promiselerde eğer işlem başarılı olursa then( ), başarısız olursa ve hata durumu olursa catch( ) fonksiyonlarının içine girer. Bu yapıya ise “Promise Chain” denir.

Gördüğünüz gibi A,B, C harflerini sırayla yazdırmayı başardık. Kod yapısına baktığımızda ise ilk fonksiyonun diğer fonksiyonun sonucunu return ettiğini ve sonucun bir sonraki fonsiyona gönderildiğini görürüz.

Diğer kısma geçmeden önce promise yapısı ile ilgili promise.all( ) ve promise.race( ), promise.allSettled( ) ve promise.any( ) özelliklerinden de bahsetmek istiyorum.

  • Promise.all( ), birden fazla promise tek bir then( ) ve catch( ) ile yazılabiliyor. Promise’lerden biri reject( ) olursa direk catch( )’e girer. Tüm Promise başarıyla tamamlanmasını bekler.
  • Promise.race( ), ise promiselerden en önce hangi promise tamamlanırsa onun sonucunu alır.
  • Promise.allSettled( ), tüm Promise başarılı, başarısız işletimleri bitince sonuçlarını status leri ile birlikte geriye döner.
  • Promise.any( ), bu yöntem, yerine getiren ilk promisi döndürmek için kullanışlıdır. Bir promise yerine getirildikten sonra kısa devre yapar, bu nedenle bir resolve olmuş bir promise bulduktan sonra diğer promiselerin tamamlanmasını beklemez.

Async / Await

Async/Await yapısı asynchronous işlemleri daha anlaşılması kolay bir hale getiren ECMAScript 2017 ile kullanıma sunulan promise tabanlı bir Javascript özelliğidir.
Anlaşılması kolay derken neyden bahsettiğimi açıklayayım. Şöyle ki biz geliştiriciler olarak, çoğunlukla synchrounous kod yazmaya alışkınız, yani birbiri ardına komut dizileri yazarız. Çünkü bu tarz yazım şeklinin okunması ve anlaşılması çok daha kolaydır. Callbackler ve promiseler döngüsellikleri nedeniyle bizi bu açıdan biraz yorar. Async-await tam olarak o anda yardımımıza koşar.

Şimdi yeniden addString ve addAll() fonksiyonumuza bakalım.

Gördüğünüz gibi A,B ve C harflerini sırayla yazdırmayı başardık.
Ayrıca addAll() fonksiyonu içinde geçen ’’async” sözcüğü JavaScript’in async / await sözdizimini kullandığımızı bilmesini sağlar ve Await kullanmak istiyorsanız bu sözcüğü kullanmanız şarttır. Aksi taktirde hata alırsınız.

Async-Await özelliği istek atarken ve o istekten dönen cevaplara göre işlem yapıyorsak sık kullanılan bir yapıdır. Bu nedenle konuyu da daha iyi anlamamız için son bir örnek daha vermek istiyorum.