Bu ders için video bulunmamaktadır.

Bu derse başlamak veya ilerlemenizi kaydetmek için lütfen giriş yapın veya kayıt olun.

Ders İçeriği

Giriş: Neden Eşzamansızlık?

JavaScript, temelde tek iş parçacıklı (single-threaded) bir dildir. Bu, normalde kodun satır satır, sırayla çalıştığı ve bir işlem bitmeden diğerine geçilmediği anlamına gelir. Bu durum, basit işlemler için sorun yaratmaz. Ancak, zaman alan işlemler (örneğin, bir sunucudan veri istemek, büyük bir dosyayı okumak, zamanlayıcılar kullanmak) söz konusu olduğunda, bu işlemlerin tamamlanmasını beklemek tüm programın (ve özellikle tarayıcıdaki kullanıcı arayüzünün) kilitlenmesine, yanıt vermemesine neden olur. Bu durum "blocking" (engelleme) olarak adlandırılır.

Kullanıcı deneyimini olumsuz etkileyen bu durumu aşmak için JavaScript, eşzamansız (asynchronous) programlama modelini kullanır. Eşzamansızlık, zaman alan bir işlemin arka planda başlamasını ve tamamlandığında bir bildirim mekanizmasıyla sonucun alınmasını sağlar. Bu sırada ana iş parçacığı diğer görevlere (örneğin, kullanıcı arayüzünü güncel tutmak, diğer olaylara yanıt vermek) devam edebilir.

Eşzamansız işlemleri yönetmek için JavaScript'te çeşitli mekanizmalar geliştirilmiştir. Bu derste, bu mekanizmaların en temel olanlarından biri olan callback (geri arama) fonksiyonlarını inceleyeceğiz.

Senkron vs. Eşzamansız Çalışma

Senkron (Synchronous) Çalışma

Kodlar yazıldığı sırayla, bir işlem bitmeden diğeri başlamayacak şekilde çalışır. Her satır bir sonrakini bekler.

senkron.js
console.log("İşlem 1 başladı.");

// Zaman alan (varsayımsal) senkron bir işlem
// Gerçekte tarayıcıda böyle uzun süren senkron işlem yapmak UI'ı kilitler!
function uzunSurenIslem() {
    let baslangic = Date.now();
    while (Date.now() - baslangic < 3000) {
        // 3 saniye boyunca bekle (CPU'yu meşgul et)
    }
    console.log("Uzun süren işlem bitti.");
}

uzunSurenIslem(); // Bu satır bitmeden aşağıdaki satıra geçilmez

console.log("İşlem 2 başladı.");

// Çıktı Sırası:
// İşlem 1 başladı.
// (3 saniye bekleme)
// Uzun süren işlem bitti.
// İşlem 2 başladı.

Eşzamansız (Asynchronous) Çalışma

Zaman alan işlemler arka planda başlatılır ve ana iş parçacığı bekletilmez. İşlem tamamlandığında sonuç (veya hata) genellikle bir callback fonksiyonu aracılığıyla iletilir.

Tarayıcı ortamında veya Node.js'te birçok yerleşik fonksiyon eşzamansız çalışır (örneğin, setTimeoutsetInterval, olay dinleyicileri, ağ istekleri (fetch/AJAX), dosya sistemi işlemleri (Node.js)).

eszamansiz.js
console.log("İşlem 1 başladı.");

// setTimeout eşzamansız bir fonksiyondur.
// Verilen fonksiyonu (callback) belirtilen süre (milisaniye) sonra çalıştırmak üzere zamanlar.
// Ancak bu süre boyunca programın akışını durdurmaz.
setTimeout(function() {
    // Bu callback fonksiyonu, 2 saniye sonra çalışacak
    console.log("Eşzamansız işlem (setTimeout) tamamlandı.");
}, 2000); // 2000 milisaniye = 2 saniye

console.log("İşlem 2 başladı.");

// Çıktı Sırası:
// İşlem 1 başladı.
// İşlem 2 başladı.
// (yaklaşık 2 saniye sonra)
// Eşzamansız işlem (setTimeout) tamamlandı.

Gördüğünüz gibi, setTimeout içindeki fonksiyon hemen çalışmadı. Program İşlem 2 başladı. mesajını yazdırdı ve ancak 2 saniye geçtikten sonra setTimeout'un callback fonksiyonu çalıştırıldı. Bu sırada ana iş parçacığı engellenmedi.

Callback Fonksiyonları Nedir?

Callback (geri arama) fonksiyonu, başka bir fonksiyona argüman olarak geçirilen ve o fonksiyonun içinde (genellikle belirli bir işlem tamamlandıktan sonra) çağrılan fonksiyondur.

Eşzamansız programlamada callback'ler, zaman alan bir işlem bittiğinde ne yapılacağını belirtmek için kullanılır. İşlemi başlatan fonksiyona, işlem bittiğinde çağrılacak olan bir callback fonksiyonu veririz.

Basit Callback Örneği

callback_ornek.js
// Hesaplama yapan ve sonucu bir callback ile bildiren fonksiyon
function hesapla(a, b, callback) {
    console.log("Hesaplama başlıyor...");
    // (Varsayımsal olarak zaman alan bir işlem)
    setTimeout(function() {
        const toplam = a + b;
        console.log("Hesaplama bitti.");
        // İşlem bitince callback fonksiyonunu çağır ve sonucu ilet
        callback(toplam);
    }, 1500); // 1.5 saniye sonra
}

// Callback fonksiyonu: Hesaplama sonucuyla ne yapılacağını tanımlar
function sonucuGoster(sonuc) {
    console.log("Hesaplama sonucu:", sonuc);
}

// Hesapla fonksiyonunu çağırırken sonucuGoster fonksiyonunu callback olarak ver
console.log("Hesaplamayı başlatıyorum.");
hesapla(10, 5, sonucuGoster);
console.log("Hesaplama başlatıldı, sonuç bekleniyor...");

// Çıktı:
// Hesaplamayı başlatıyorum.
// Hesaplama başlıyor...
// Hesaplama başlatıldı, sonuç bekleniyor...
// (1.5 saniye sonra)
// Hesaplama bitti.
// Hesaplama sonucu: 15

Bu örnekte, hesapla fonksiyonu bir işlem yapar (setTimeout ile simüle edildi) ve işlem bittiğinde kendisine verilen callback fonksiyonunu (yani sonucuGoster'i) çağırarak sonucu iletir.

Hata Yönetimi ile Callback'ler

Eşzamansız işlemler sırasında hatalar oluşabilir (örneğin, ağ bağlantısı kopması, dosya bulunamaması). Callback deseninde hata yönetimi genellikle callback fonksiyonuna ilk argüman olarak bir hata nesnesi geçirilerek yapılır. Eğer hata yoksa bu argüman null veya undefined olur.

callback_hata.js
// Veri getirme işlemini simüle eden fonksiyon
function veriGetir(id, callback) {
    console.log(`${id} ID'li veri getiriliyor...`);
    setTimeout(function() {
        // Rastgele hata oluşturma simülasyonu
        const hataOlustu = Math.random() > 0.5;
        
        if (hataOlustu) {
            // Hata durumunda callback'i hata nesnesiyle çağır
            const hata = new Error("Veri getirilemedi!");
            callback(hata, null); // İlk argüman hata, ikinci argüman veri (null)
        } else {
            // Başarılı durumda veriyi oluştur
            const veri = { id: id, deger: `Veri ${id}` };
            // Callback'i hata olmadan (null) ve veriyle çağır
            callback(null, veri); // İlk argüman null, ikinci argüman veri
        }
    }, 1000);
}

// Callback fonksiyonu (hata kontrolü ile)
function islemSonucu(hata, veri) {
    if (hata) {
        console.error("Hata oluştu:", hata.message);
        // Hata durumunda yapılacaklar...
    } else {
        console.log("Veri başarıyla alındı:", veri);
        // Başarılı durumda yapılacaklar...
    }
}

// Fonksiyonu çağırma
veriGetir(123, islemSonucu);
veriGetir(456, islemSonucu);