Yazılım.
CevapSitesi.com Beta!
Çözüm Noktası
Facebook, Twitter, Google+ veya e-posta ile paylaşın.
| Sorular | Makaleler | Üyeler | Etiketler  | İletişim
Soru sormak ya da cevap vermek için;
giriş yapın veya üye olun.

Sosyal medya hesaplarınızla da giriş yapabilirsiniz.
0

JavaScript Fonksiyonları: Üretici Fonksiyonlar - Generator Functions

(Bu yeni bir standarttır. Tüm tarayıcılarla uyumlu değildir. Ancak Node.js gibi bazı uygulamalarda çalışır.)

Üretici fonksiyonlar bir yineleyici blok (döngü veyafor .. of gibi) içinden tekrarlı çağırıldıklarında ya da ayrı ayrı her çağırıldıklarında bir değer döndüren fonksiyonlardır. Döndürdüğü değerler genellikle birbiriyle ilgili bir dizinin elemanları, bir sayı silsilesinin sayıları gibi ardışık ve ilişkili değerlerdir.

Uretici fonksiyon tanımlanırken function bildiriminin yanına veya fonksiyon adının önüne bir * karakteri konur.

function* yineleyici() {
	...
}

Bu fonksiyonlar değer döndürmek için return   ifadesi yerine yield (teslim et) ifadesini kullanırlar. Her bir yield, fonksiyonun çalışmasını durdurur ve çağıran fonksiyona bir değer döndürür. Üretici fonksiyonun sonraki çağılmasında son çalışan yield'den sonraki yield çalıştırılıp yanında belirtilen değer döndürülür ve bu şekilde devam eder.

function* yineleyici() {
	yield 1
	yield 3
	yield 5
}

Üretici fonksiyonları kullanabilmek ve yield ile veri döndürmesini sağlayabilmek için fonsiyon normal olarak çağırılır. Bu çağrıdan bir Generator nesnesi elde ederiz. Dönen Generator referans değeri bir değişkene atanır. Bunun ardından fonksiyondan değer almak için Generator örneğinin (değişkenin) next metodu çağırılır.

var uretici = yineleyici(); // Bir Generator nesnesi döndürür. Değişkene atıyoruz.
console.log(uretici.next()); // Sıradaki yield ile dönen değeri almak için Generator nesnesinin next metodunu çağır.

next metodu, çağırıldığında value ve done özelliklerine sahip basit bir nesne döndürür.

Dönen nesneye örnek:

{ value:"2", done:false }

Bu nesnedeki value (değer) özelliğinin değeri yield ile döndürülen değeri tutar. done (bitti, tamamlandı) özelliği ise üretici fonksiyon içinde son çalıştırılan yield'den sonra başka yield olup olmadığını döndürür. Bunu fonksiyonun yaptığı üretme işleminin bitip bitmediğini anlamak için kullanırız.

Tam örnek:
function* yineleyici() {
    yield 1;
    yield 3;
    yield 5;
}

var uretici = yineleyici(); // Bir Generator nesnesi döndürür. Değişkene atıyoruz.

// İlk yield'i çalıştır, dönen nesnenin value değerini yaz.
console.log(uretici.next().value);  // next, { value: 1, done: false } nesnesini verir.

// İkinci yield'i çalıştır, dönen nesnenin value değerini yaz.
console.log(uretici.next().value);  // next, { value: 3, done: false } nesnesini verir.

// Üçüncü yield'i çalıştır, dönen nesnenin value değerini yaz.
console.log(uretici.next().value);  // next, { value: 5, done: false } nesnesini verir.

var sonAlinanDeger = uretici.next(); // Başka yield olmadığından { value: undefined, done: true } nesnesi döner. 

console.log(sonAlinanDeger.value); // Başka yield olmadığından undefined değeri
console.log(sonAlinanDeger.done); // Başka yield olmadığından true değeri

Çıktısı:

1
3
5
undefined
true

Üretici fonksiyonlarda yield değer döndürmek zorunda değildir. Eğer sadece yield kullanılırsa undefined döndürür. Bu yüzden değere bakarak fonksiyonun döndüreceği değerlerin tamamlanıp tamamlanmadığını anlamaya çalışmayın. Bitip bitmediğini anlamak için done değerini test edin.

Eğer hem değer almak ve hem de yinelemenin bittiğini haber almak isterseniz ilk ve son kez bir return ile değer döndürebilirsiniz. return ile değer döndürüldüğünde done değeri true olur. Ancak bundan sonra next'i kullanırsak hata ortaya çıkar.
function* yineleyici() {
	yield 1;
	yield 3;
	return 5;
}

var uretici = yineleyici();

console.log(uretici.next().done);
console.log(uretici.next().done);

var sonAlinanDeger = uretici.next();

console.log(sonAlinanDeger.value);
console.log(sonAlinanDeger.done);

Çıktısı:

false
false
5
true

Generator nesnesine, dolayısıyla üretici fonksiyona parametre olarak değer gönderebiliriz. Bunun için Generator nesnesini elde etmek için fonksiyonu çağırdığınızda fonksiyona göndereceğiniz parametreyi de bildirin.

function* yineleyici(carpan) {
	yield 1 * carpan;
	yield 3 * carpan;
	yield 5 * carpan;
}

var uretici = yineleyici(2);

console.log(uretici.next().value);
console.log(uretici.next().value);
console.log(uretici.next().value);

Çıktısı:

2
6
10

Üretici fonksiyondan oluşturulan her bir Generator nesnesi üretici fonksiyonun ayrı bir kopyasını çalıştıracaktır.

function* yineleyici() {
    yield 1;
    yield 3,
    yield 5;
}

var uretici1 = yineleyici();
var uretici2 = yineleyici();

console.log(uretici1.next().value);
console.log(uretici2.next().value);

Çıktısı:

1
1

Üretici fonksiyonlar, başta da belirttiğimiz gibi ardışık ve alakalı bir değerler kümesinin elemanlarını tek tek döndürmek için uygundur.

Bu örnek, Fibonacci dizisinin elemanlarını sırayla döndürür:
function* fibonacci() {
	var dizi = [1, 1, 2, 3, 5, 8, 13, 21];
	for (var i=0; i<dizi.length; i++)
		yield dizi[i];
}

var uretici = fibonacci();

console.log(uretici.next().value);
console.log(uretici.next().value);
console.log(uretici.next().value);

Çıktısı:

1
1
2

Üretici fonksiyonları yinelenebilir (iterable) olduğundan, bu tür sınıfların (Array , arguments , String vs.) elemanlarını gezmek için kullanılan for ... of  döngüsüyle beraber kullanılabilir.

function* fibonacci() {
    var dizi = [1, 1, 2, 3, 5, 8, 13, 21];
    for (var i=0; i<dizi.length; i++)
        yield dizi[i];
}

var uretici = fibonacci();

for (var sayi of uretici) // var yerine let de yazabilirsiniz.
    console.log(sayi);
Çıktısı:

1
1
2
3
5
8
13
21

Üretici fonksiyonların sonraki tekrarını next ile çağırdığınızda fonksiyona bir değer gönderebilirsiniz. Bu parametre göndermekten farklıdır. Parametreyi sadece bir kez gönderebilirsiniz. next ile her seferinde fonksiyonun yield ile bekleyen satırına farklı bir değer gönderebilirsiniz.

next ile fonksiyona bir değer gönderildiğinde bu değer yield ile alınır.

Burada şuna dikkat edin. next ile gönderilen değerin alınabilmesi için bekleten bir  yield gereklidir. Yani hiç bekleten bir yield yoksa gönderdiğiniz değer bir işe yaramaz. Ama eğer bir kez next kullanmışsanız (varsa) bir yield bekliyordur. Bundan sonraki next çağırınızla gönderdiğiniz değer bekleyen yield ile döndürülecektir. Bu aşamada değeri bir değişkene atayabilir veya başka şekilde kullanabilirsiniz.

function* selamla() {
    var adi = yield "Adınız?";
    yield "Selam " + adi;

}

var uretici = selamla();

console.log(uretici.next().value); // "Adınız?" yazar. Bir sonraki next kullanımında fonksiyondaki adi değişkenine atanacak bir değer gönderilmeli.
console.log(uretici.next("Ali").value); // "Selam Ali" yazar.
İlk next çağrısının parametresiz yapıldığına dikkat edin. Bu çağrıyı bir yield bekletmesi oluşturmak için yaptık. İkinci next çağrımızda, next ile gönderdiğimiz "Ali" değeri bekleyen yield'in (ilk yield ) döndürdüğü bir sonuç olduğu için bunu adi değişkenine atadık. Ardından ikinci yield çalıştı ve selamlama metnini gönderdi.

Bir üretici fonksiyon içinden başka bir üretici fonksiyonu çağırabilirsiniz. Bunun için yield*  kullanılır. Bu durumda üretici fonksiyon içinden çağırılan üretici fonksiyonun yield'leri bitene kadar çağıran fonksiyon yield* satırında bekler.

Örnek:

function* deneme2() {
    yield "a";
    yield "b";
}


function* deneme() {
    yield 1;
    yield* deneme2();
    yield 2;
}


for (let deger of deneme())
    console.log(deger);

Çıktısı:

1
a
b
2

Üretici fonksiyonlar yerinde çalıştırılan isimsiz fonksiyonlar olarak da yazılabilir. Üretici fonksiyonlar yapılandırıcı olarak kullanılamazlar.



JavaScript Fonksiyonları ile İlgili Makaleler: