Javascript’te Promise Kullanımı

Özgün Bal
Codefiction
Published in
4 min readNov 30, 2017

Detaylı teknik yazıya başlamadan önce, şunu belirtmek isterim ki internette promise yapısını açıklayan pek çok ingilizce faydalı kaynak bulunmakta. Türkçe olarak da ben elimden geldiğince açıklamaya çalışacağım. Nelerden bahsedeceğimi kısaca baştan belirteyim:

  • En basit haliyle promise nedir? Ne işe yarar?
  • Callback yerine promise kullanmanın avantajları nelerdir?
  • Promise’lerin sözdizimi(syntax)
  • Promise zincirleri
  • Promise metodları
  • …ve async/await
Sözümüz sözdür.

Nedir Promise?

Şu yazıdan alınmış bir örnekle başlayayım. Diyelim ki bir hamburgerciye gittiniz ve kasada siparişinizi verdiniz. Kasiyer de size siparişinizi hazırladıklarında haber vermek üzere bir elektronik alet teslim etti. Şu anda hamburger bizim için gelecekte elde edebileceğimiz bir değer. Elimizdeki aygıt da bu değere ulaşmamız için müessesenin bize verdiği sözün (işte promise) bir göstergesi. Aygıt bildirim alıncaya kadar bekleme (pending) durumundadır. Bildirim geldiğinde ise ya hamburgerimiz başarılı (resolved) bir şekilde hazırlanmıştır, ya da beklenenin dışına çıkarak başarısız (rejected) olmuştur. Başarılı durumda afiyetle yemeğimize yumuluruz tabii ki. Başarısız durum biraz daha nahoş olacaktır. Kasiyerle kavga ederek veya yeniden sipariş vererek hatalı durumu yönetmemiz (error handling) gerekir.

Callback mi döver, Promise mi?

Öncelikle hatırlayalım callback neydi diye. Callback, en basit haliyle herhangi bir fonksiyona parametre olarak verdiğimiz ve sonra geri çağıracağımız fonksiyonlara denir. İstenilen değere ulaştığında veya işlem sonlandığında görevini yerine getirir.

Buradaki sıkıntılı nokta aslında, callback’imizi doğru ellere mi teslim ediyoruz sorusu. Emanete hıyanet ederler mi, callback’imizi çağırmadan yaban ellerde (başka bir API fonksiyonunda mesela) bırakırlar mı? Şakası bir yana callback yapısıyla ilgili bir güven sorunu olduğu aşikar. Güvenemememizin başlıca sebepleri şunlar:

  • 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 yutulması

Promise, callback’lerin sıkıntılı yönlerini düzeltmek amacıyla önerilmiş bir yapıdır. Sözdiziminden bahsettiğimde daha net akıllarda oturacaktır ancak şimdiden avantajlarından bahsetmekte fayda var. Şöyle ki:

  • Promise istenilen görevi yerine getirdiğinde değeri değişmez (immutable)
  • Sadece bir kere başarıya (resolved) ulaşır, veya başarısız (rejected) olur.
  • Öngörülemeyen hatalar otomatik olarak Promise’i başarısız (rejected) sonuca götürür. Bu da hataları kontrol etme noktasında faydalıdır.
  • Yapısı gereği, gelecekteki bir değerin göstergesi olduğundan daha güvenilirdir.

Detaylı açıklama için: You Don’t Know Javascript — Async & Performance

Promise Sözdizimi

Geldik anlatılan konseptlerin daha elle tutulur tarafına. Kodu görünce, eminim soyut kavramlara göre kendinizi daha rahat hissedeceksinizdir.

En basitinden bir promise örneği:

  • Promise’leri new öneki ile tanımlıyoruz.
  • Başarılı (resolve) ve başarısız (reject) durumlarda çağıralacak iki fonksiyon ile birlikte oluşturuyoruz. (Sıralamayı değiştirmemek kaydıyla bu iki fonksiyona farklı isimler de verebilirsiniz ancak genelde bu isimler tercih edilmektedir.)
  • Promise’leri bir değişkene atayabiliriz. (Örnekteki sozVerdik değişkeni gibi.)
  • Promise beklenilen işlemi gerçekleştirdikten sonra yapılacak adımlar için .then() fonksiyonu çağırılır. İçerisindeki fonksiyonun parametresi resolve() ile gönderilen parametredir.
  • Eğer istek dahilinde reject() çağırıldığında veya öngörülemeyen bir hata sonucu promise başarısız olduğunda .then() fonksiyonu es geçilerek, .catch() içerisindeki fonksiyon çağırılır ve hatalı durumda yapılacak adımlar izlenir.

Promise Zincirleri

Zincir diyerek kastettiğim şu: biri diğerini bekleyen asenkron işlemlerin arka arkaya çalıştırılması. Örneğin, cep telefonunda kullandığımız bir uygulamaya güncelleme geldiğinde, önce güncellemenin tamamlanması bekleriz, ardından uygulamadan içerik talep edebiliriz. Promise yapısı gereği asenkrondur ve uygulamadaki bekleyen diğer kodların çalışmasını bekletmez. Bu yüzden sıralı asenkron işlemlerin birbirini beklemesi için promise zinciri diyebileceğimiz yapılar oluşturmamız gerekir.

Yukarıdaki kod parçacığı üzerinden promise zincilerine bir bakalım:

  • Birden fazla .then() arka arkaya eklenerek oluşturulur.
  • Zinciri başlatan bir promise olduğu gibi .then() içindeki fonksiyonların dönüş değeri de promise olur. return değeri promise’leştirilerek zincirin diğer halkasına aktarılır. Bu yüzden zincirin her bir halkasını promise okuyan ve promise çıktısı oluşturan bir yapı olarak düşünebiliriz.
  • Hataların yakalanması için tek bir .catch() yeterlidir. Zincir içerisinde ne zaman başarısız (rejected) bir promise veya beklenmedik bir hata oluşursa, sonrasındaki .then() halkaları atlanılarak .catch() içerisindeki fonksiyon çalışır. asenkronIslem, baskaAsenkronIslem, birinci veya ikinci .then()’in hatalı sonuçlanması konsola hata mesajının basılması ile sonuçlanır.

Promise metodları

Promise nesnesinin kendine ait 4 tane metodu bulunmaktadır. Bunlar yeni bir promise (new Promise()) oluşturmadan kullanılabilir. Açıklamadan önce, örnek kullanımlarına bakalım:

  • Promise.resolve(): Verilen değeri, başarılı sonuçlanmış promise haline getirir. .then() içindeki fonksiyonların dönüş değeri kendiliğinden promise haline getirilir demiştik. Bu durumu `return Promise.resolve(donusDegeri)` şeklinde de düşünebiliriz.
  • Promise.reject(): Verilen değer, hata mesajı olacak şekilde başarısız(rejected) olmuş bir promise nesnesi döndürür.
  • Promise.all(): Bu metodun kullanılma amacını, paralel olarak gerçekleştirilen asenkron işlemlerin hepsinin bitip, bitmediği anlamak olarak tanımlayabiliriz. Birbirini beklemeyen asenkron işlemleri (promise’leri) zincirlersek toplam gerçekleşme süresini uzatmış oluruz. Hem bekleyen tüm işlemlerin bittiğinden emin olmak hem de bunu promise kullanarak yapabilmek Promise.all() ile mümkün. Dizideki tüm promise’ler başarılı(resolved) olduğunda tuttukları değerler de dizi halinde kullanıcıya döner. Eğer dizideki herhangi bir promise başarısız(rejected) olursa, Promise.all() sonucu oluşan promise de başarısız(rejected) olur.
  • Promise.race(): Bu metod ise adından anlaşılacağı gibi dizi içerisindeki promise’lerin yarıştırılmasıdır. Galip olan, yani en hızlı sonuca ulaşan promise dönüş değeri olarak alınır. Yalnız başarılı(resolved) veya başarısız (rejected) sonuçlanması sonucu etkilemez. Gelecekteki vaat edilen değerine (istenilen değer veya hata mesajı) ilk ulaşan promise yarışın kazananı olur.

Async/Await

Öncelikle bu özellik ES7 kapsamında olup, promise’e nazaran tarayıcı desteği daha azdır. Eğer NodeJS 8 üstü kullanıyorsanız, ya da transpiler’a (bkz: babel) güvenerek bu özelliği denerim diyorsanız, anlatacaklarıma kulak verin.

Async/await bize neler katacak? Maddelerimiz şöyle:

  • Kod daha okunaklı hale gelecek
  • Asenkron işlemlerin gerçekleşme sırasını takip etmek kolaylaşacak
  • Promise zincirleri için birçok .then() yazmak gerekmeyecek
  • Diğer programlama dillerinden aşina olunan try-catch yapısı kullanılabilecek

Yukarıdaki iki farklı kod da aynı işlemleri gerçekletirmektedir. İki yöntem de promise dönen asenkron işlemleri çağırır. await, promise dönen fonksiyonların öncesinde kullanılarak değerin gelmesini bekletir. Ancak await sadece async ile tanımlanmış bir fonksiyon içerisinde kullanılabilir. async ile tanımlanmış asenkron bir fonksiyonun dönüş değeri de promise nesnesi olur.

Async/await tek başına daha detaylı da anlatılabilir ama bu yazı özelinde bu kadar değinmem yeter diye düşünüyorum. Amaç, promise kullanmanın güzelliğini bir açıdan daha göstermekti. Siz yine hobi olarak async/await kullanın ben ona karışmıyorum ancak konumuz en başından beri promise olduğu için burada noktalamam yeterli olacaktır.

Eğer siz de asenkron işlemlerde güven problemi yaşıyorsanız, callback’lerim geri dönmüyor, ne yapmam lazım diyorsanız; promise kullanın, kafanız rahat etsin.

--

--