Skip to content

Instantly share code, notes, and snippets.

@f
Created November 7, 2012 14:30
Show Gist options
  • Save f/4031934 to your computer and use it in GitHub Desktop.
Save f/4031934 to your computer and use it in GitHub Desktop.
Promise Algoritması

Promise Algoritması

Promise, asenkron programlamanın temel taşlarından birisidir. İlk okuduğum kaynaklarda mantığını tam olarak anlamamış olsam da daha sonradan aslında pek de zor bir konu olmadığını öğrendim.

Promise, adı üzerinde "söz" bir fonksiyonun bir olayı gerçekleyeceğine söz vermesidir.

Kendimizden örnek

Gerçek dünyadan bir promise örneği düşünelim;

"Ali ekmek alacak, Veli de süt alacak" gibi bir ilkokula giriş cümlesi ile başlayalım.

Bu cümlede Ali, bize ekmek alacağı sözünü veriyor; Veli ise süt alacağı sözünü veriyor.

"Ali ekmek alırsa, ona para veririm."

"Veli süt alamazsa, ona çok kızarım."

şeklinde iki adet de verilen sözlerin karşılığında yapılacak işleri düşünelim.

Sanki çok "if/else" gibi?

Evet, buraya kadar işler fazlasıyla "if" koşulu gibi duruyor, fakat "if" koşulu asenkron bir mantıkta çalışmaz.

Bunu if ile yazsaydık;

if (aliEkmekAlsin()) {
  paraVer();
}
if (!veliSutAlsin()) {
  cokKiz();
}

Burada işlemlerin bitmesi gerektiğini görüyoruz; promise yapılarda bu işlerin bitmesi gerekmiyor. Her iş bitişi için bir promise belirliyoruz.

Bunu asenkron düşündüğümüzde şöyle bir yapıda yazabiliriz;

aliEkmekAlsin().success(function () {
  paraVer();
});
veliSutAlsin().fail(function () {
  cokKiz();
});

If koşullamasından biraz kurtulduk. Peki bu şekilde yazmak bize ne kazandırdı? Öncelikle, if kullanmadığımız için, işlemin sonucunu beklemiyoruz, çok uzun sürse dahi. Bunun yerine yapılacak işlemin sonucuna bağlı birer callback atıyoruz.

Esas çocuk: promise

Evet, geldik işin en can alıcı kısmına. Bu fonksiyonlar .success, .fail gibi durumları nasıl dönüyorlar?

yap().success(function () { baskaBirSeyYap(); });

Cevap: promise ile. Bu fonksiyonlar, implemente edilirken, promise dönecek şekilde yazılıyorlar.

Bakalım aliEkmekAlsin fonksiyonumuz nasıl implemente edilmiş?

aliEkmekAlsin = function () {
  var deferred = new promise();
  
  $.ajax('/bakkal/ekmek', {}, {
    success: deferred.resolve,
    error: deferred.reject
  });
  return deferred;
};

Evet! Ali bakkaldan ekmek alırsa, promise çözülsün (sözünü tutmuş olsun), yoksa promise iptal olsun (sözünü tutmadı :()

Ups! işler karıştı?

Hayır, karışmadı. Şimdi bunu daha çalışabilir bir hale getirelim:

aliEkmekAlsin = function () {
  var deferred = new promise();
  setTimeout(deferred.resolve, 2000);
  
  // promise methodlarını kullanmamız gerekiyor, yoksa sonradan callback bağlayamayız.
  return deferred;
}
aliEkmekAlsin().success(function () {
  alert('aferin ali!'); // para vermiyoruz tabii ki:P
});

O zaman bunu çalıştıracak promise sınıfımızın en ilkel halini yazalım:

function promise() {
  var self = this;
  var success = function() {};
  var fail = function () {};
  
  this.resolve = function() {
    success();
  };
  this.reject = function() {
    fail();
  };
  
  this.success = function (_success) {
    success = _success;
    return self;
  };
  this.fail = function (_fail) {
    fail = fail;
    return self;
  };
}

Bütün temeli bu kadar. Gerçekten.

Şu an bir promise sınıfına sahibiz. Fakat biraz fazla yeteneksiz. Geliştireceğiz.

Öncelikle bunun bir hatası var, yalnızca bir success callback'i alabiliyor. Bunu biraz daha birikebilir hale getirelim:

function promise() {
  var self = this;
  var success = [];
  var fail = [];
  
  this.resolve = function() {
    var i = 0;
    while (_success = success[i++]) {
      _success();
    }
  };
  this.reject = function() {
    var i = 0;
    while (_fail = fail[i++]) {
      _fail();
    }
  };
  
  this.success = function (_success) {
    success.push(_success);
    return self;
  };
  this.fail = function (_fail) {
    fail.push(_fail);
    return self;
  };
}

Evet işte oldu. Bu bizim promise algoritmamızın temelini oluşturuyor. Ve aslında çalışan bir promise algoritması. :)

Güzeel, birden fazla promise'i yönetmek nasıl olur peki?

Evet, orası için de fikrimiz var.

Elimizde bir sürü deferred (promise dönen) nesne olsun;

when (aliEkmekAlsin(), veliSutAlsin()).success(function () {
  alert("aferin ali ve veli!");
});

diyerek alabilmeliyiz;

O halde hemen when algoritmasının en ilkel halini yazalım:

function when(ilk, ikinci) {
  var self = this;
  var resolved = 0;
  
  var success = [];
  var fail = [];
  
  var successOrFail = function () {
    if (resolved == 2) {
      while (_success = success[i++]) {
        _success();
      }
    } else {
      while (_fail = fail[i++]) {
        _fail();
      }
    }
  };
  
  ilk.success(function () {
    resolved++;
    successOrFail();
  }).fail(function() {
    successOrFail();
  });
  
  ikinci.success(function () {
    resolved++;
    successOrFail();
  }).fail(function() {
    successOrFail();
  });
  
  this.success = function (_success) {
    success.push(_success);
    return self;
  };
  this.fail = function (_fail) {
    fail.push(_fail);
    return self;
  };
  return this;
}

Biraz uzun oldu, ama en temel hali bu. Kısaca şunu yapıyor, her promise'a bir success callback atayıp, bu callback'lerin bir pointer'ı (resolved) bir artırıyor. Böylece kaç adet resolve olduğunu biliyoruz. Eğer resolved ile toplam promise sayısı eşit ise, bu işlem başarılı oluyor. Aksi halde başarısız olarak dönüyor.

when (aliEkmekAlsin(), veliSutAlsin()).success(function () {
  alert("aferin ali ve veli!");
});

artık çalışır durumda.

Fakat bu örnek yalnızca iki deferred için çalışıyor. O zaman bunu biraz daha dinamik yapalım.

function when() {
  var self = this;
  var resolved = 0;
  
  var args = arguments;
  var deferreds = Array.prototype.slice.call(args);
  
  var success = [];
  var fail = [];
  
  var successOrFail = function () {
    var i = 0;
    if (resolved == deferreds.length) {
      while (_success = success[i++]) {
        _success();
      }
    } else {
      while (_fail = fail[i++]) {
        _fail();
      }
    }
  };
  
  var i = 0;
  while (deferred = deferreds[i++]) {
    deferred.success(function () {
      resolved++;
      successOrFail();
    });
  }
  
  this.success = function (_success) {
    success.push(_success);
    return self;
  };
  this.fail = function (_fail) {
    fail.push(_fail);
    return self;
  };
  return this;
}

Şimdi deneyelim:

when (aliEkmekAlsin(), veliSutAlsin()).success(function () {
  alert("aferin ali ve veli!");
});

Evet, çalışıyor. :)

Promise işleri bu kadar kolay. Tabii, bunun daha güçlü implementasyonlarını yazmak mümkün. Eğer jQuery kullanıyorsanız, hazır implemente edilmiş bir hali zaten var.

@yuxel
Copy link

yuxel commented Nov 7, 2012

while (_success = success[i++]) { 

gibi bir şeyde _success global tanımlanıyor, can yakabilir ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment