Modern web uygulamaları, kullanıcı deneyimini kesintiye uğratmadan arka planda veri çekme, dosya işleme ve diğer zaman alıcı operasyonları gerçekleştirmek zorundadır. Bu tür asenkron işlemleri yönetmek, JavaScript'in en önemli konularından biridir. Geleneksel olarak, asenkron işlemler callback (geri arama) fonksiyonları ile yönetilirdi. Ancak, iç içe geçmiş callback'ler (callback hell veya pyramid of doom olarak bilinir), kodun okunabilirliğini ve bakımını zorlaştırıyordu. İşte tam da bu noktada Promise API devreye girer ve asenkron programlamaya çok daha zarif ve yönetilebilir bir yaklaşım sunar.
Promise Nedir?
Bir Promise (Söz), asenkron bir işlemin nihai sonucunu (başarılı tamamlanması veya başarısız olması) temsil eden bir JavaScript nesnesidir. Bir Promise, üç ana durumdan birinde olabilir:
Bir Promise, 'pending' durumundan 'fulfilled' veya 'rejected' durumuna geçer ve bir kez geçtikten sonra durumu bir daha değişmez. Bu özelliği, asenkron operasyonların öngörülebilirliğini artırır.
Temel Promise Oluşturma ve Kullanımı
Yeni bir Promise oluşturmak için `new Promise()` yapıcısını kullanırız. Bu yapıcı, iki argüman alan bir yürütme fonksiyonu alır: `resolve` ve `reject`.
Bu örnekte, `setTimeout` ile simüle edilen bir asenkron işlemimiz var. İşlem başarılı olursa `resolve()`, hata olursa `reject()` çağrılır.
`Promise.resolve()` ve `Promise.reject()`
Bazen hemen çözülen veya hemen reddedilen bir Promise'e ihtiyacımız olabilir. Bu durumda `Promise.resolve()` ve `Promise.reject()` statik metotları kullanılır.
Promise Zincirleme (.then(), .catch(), .finally())
Promise'lerin en güçlü özelliklerinden biri, art arda sıralanabilmeleridir. Her `.then()` çağrısı yeni bir Promise döndürür, bu da zincirleme yapmaya olanak tanır. Bu sayede, bir işlemin sonucuna bağlı olarak başka bir işlem başlatabiliriz.
.then() Metodu: Promise başarıyla tamamlandığında çalışacak kodu tanımlar. Bir veya iki fonksiyon alabilir: `onFulfilled` ve `onRejected`. Genellikle sadece `onFulfilled` kullanılır ve hatalar için `.catch()` tercih edilir.
.catch() Metodu: Promise zincirindeki herhangi bir noktada meydana gelen hataları yakalamak için kullanılır. En iyi uygulama, zincirin sonuna bir `.catch()` bloğu eklemektir. Bu, zincirdeki tüm Promise'lerden fırlatılan hataları tek bir noktada ele almanızı sağlar.
.finally() Metodu: Promise'in durumu ne olursa olsun (başarılı veya reddedilmiş) çalıştırılacak kodu tanımlar. Kaynakları serbest bırakma, yükleme göstergelerini kapatma gibi temizlik işlemleri için idealdir.
Gelişmiş Promise Metotları
Birden fazla Promise'i aynı anda yönetmek için Promise API, bir dizi statik metot sunar:
Promise.all(iterable): Bir dizi Promise'i alır ve tüm Promise'ler başarılı olduğunda çözülen tek bir Promise döndürür. Eğer herhangi bir Promise reddedilirse, `Promise.all` hemen reddedilir ve ilk reddedilen Promise'in hatasını döndürür.
Promise.race(iterable): Bir dizi Promise alır ve bu Promise'lerden ilk tamamlanan (çözülen veya reddedilen) ile aynı değeri/hatayı döndürür. Yarışan Promise'lerden hangisi önce biterse, onun sonucunu döndürür.
Promise.allSettled(iterable): (ES2020) Bir dizi Promise alır ve tüm Promise'ler çözüldüğünde (başarılı veya reddedilmiş olsalar bile) çözülen tek bir Promise döndürür. Sonuç, her Promise'in durumunu (status: 'fulfilled' veya 'rejected') ve değerini (value) veya hatasını (reason) içeren bir nesneler dizisidir.
Promise.any(iterable): (ES2021) Bir dizi Promise alır ve Promise'lerden herhangi biri başarılı olduğunda çözülen tek bir Promise döndürür. Eğer tüm Promise'ler reddedilirse, bir `AggregateError` ile reddedilir.
Hata Yönetimi ve En İyi Pratikler
* Her Zaman Bir `.catch()` Ekleyin: Promise zincirinizin sonuna bir `.catch()` bloğu eklemek, beklenmeyen hataların uygulamanızı çökertmesini engeller ve hata ayıklamayı kolaylaştırır.
* Gereksiz İç İçe Promise'lerden Kaçının: Callback cehennemine benzer şekilde Promise cehennemine düşmemek için `.then()` zincirleme özelliğini etkin kullanın. Her `.then()` bloğundan yeni bir Promise döndürerek kodu düz tutun.
* Senkron Kodları Promise İçine Koymayın: Promise'ler asenkron işlemler içindir. Senkron kodları Promise yapısı içine koymak performansa fayda sağlamaz, aksine okunurluğu azaltır.
* Hataları Yeniden Fırlatın (Re-throw Errors): Bazen bir `.catch()` bloğunda hatayı işledikten sonra, hatanın zincirde yukarı doğru yayılmaya devam etmesini isteyebilirsiniz. Bu durumda, hatayı yeniden fırlatabilirsiniz (`throw error;`).
Async/Await ile Promise Kullanımı
ES2017 ile tanıtılan `async/await` sözdizimi, Promise'ler üzerinde daha okunabilir ve senkron görünümlü kod yazmamızı sağlar. Aslında `async/await`, Promise'ler üzerinde bir 'sentaktik şeker'dir ve altında yine Promise'leri kullanır. Bu sayede, uzun Promise zincirlerini daha lineer bir akışla yazabiliriz.
Sonuç
Resim: Promise akış şeması (temsili)
Promise API, JavaScript'teki asenkron programlamayı kökten değiştiren ve modern web geliştirmenin temel taşlarından biri haline gelen güçlü bir yapıdır. Callback hell sorununu çözmekle kalmayıp, kodu daha okunaklı, bakımı kolay ve hatalara karşı daha dirençli hale getirir. `Promise.all`, `Promise.race`, `Promise.allSettled` ve `Promise.any` gibi gelişmiş metotlar, karmaşık asenkron senaryoları yönetmek için esneklik sunar. `async/await` ile birleştiğinde ise, asenkron kod yazımı neredeyse senkron kod kadar anlaşılır hale gelir. Bu konuyu daha derinlemesine incelemek için MDN Promise belgesini ziyaret edebilirsiniz. Promise'lere hakim olmak, modern JavaScript geliştiricisinin olmazsa olmazıdır.
Umarım bu kapsamlı rehber, Promise API'nin gücünü ve kullanımını anlamanıza yardımcı olmuştur. İyi kodlamalar!
Promise Nedir?
Bir Promise (Söz), asenkron bir işlemin nihai sonucunu (başarılı tamamlanması veya başarısız olması) temsil eden bir JavaScript nesnesidir. Bir Promise, üç ana durumdan birinde olabilir:
- Pending (Beklemede): Başlangıç durumu, ne başarıyla tamamlanmış ne de reddedilmiş.
- Fulfilled (Yerine Getirilmiş): İşlem başarıyla tamamlandı ve bir değer döndürdü.
- Rejected (Reddedildi): İşlem başarısız oldu ve bir hata döndürdü.
Bir Promise, 'pending' durumundan 'fulfilled' veya 'rejected' durumuna geçer ve bir kez geçtikten sonra durumu bir daha değişmez. Bu özelliği, asenkron operasyonların öngörülebilirliğini artırır.
Temel Promise Oluşturma ve Kullanımı
Yeni bir Promise oluşturmak için `new Promise()` yapıcısını kullanırız. Bu yapıcı, iki argüman alan bir yürütme fonksiyonu alır: `resolve` ve `reject`.
Kod:
const myFirstPromise = new Promise((resolve, reject) => {
// Asenkron bir işlem başlat
setTimeout(() => {
const success = true; // Gerçek bir senaryoda bu bir API yanıtı veya dosya okuma sonucu olabilir
if (success) {
resolve('Veri başarıyla alındı!'); // Promise'i başarılı olarak işaretle
} else {
reject('Veri alma işleminde hata oluştu.'); // Promise'i reddedilmiş olarak işaretle
}
}, 2000); // 2 saniye sonra işlemi tamamla
});
myFirstPromise
.then((message) => {
console.log('[b]Başarılı:[/b]', message);
})
.catch((error) => {
console.error('[b]Hata:[/b]', error);
})
.finally(() => {
console.log('[b]İşlem tamamlandı.[/b]');
});
// Çıktı (2 saniye sonra başarılı olursa):
// Başarılı: Veri başarıyla alındı!
// İşlem tamamlandı.
Bu örnekte, `setTimeout` ile simüle edilen bir asenkron işlemimiz var. İşlem başarılı olursa `resolve()`, hata olursa `reject()` çağrılır.
`Promise.resolve()` ve `Promise.reject()`
Bazen hemen çözülen veya hemen reddedilen bir Promise'e ihtiyacımız olabilir. Bu durumda `Promise.resolve()` ve `Promise.reject()` statik metotları kullanılır.
Kod:
// Hemen çözülen bir Promise
Promise.resolve('Bu Promise hemen çözüldü.')
.then(value => console.log(value)); // Çıktı: Bu Promise hemen çözüldü.
// Hemen reddedilen bir Promise
Promise.reject(new Error('Bu Promise hemen reddedildi.'))
.catch(error => console.error(error.message)); // Çıktı: Bu Promise hemen reddedildi.
Promise Zincirleme (.then(), .catch(), .finally())
Promise'lerin en güçlü özelliklerinden biri, art arda sıralanabilmeleridir. Her `.then()` çağrısı yeni bir Promise döndürür, bu da zincirleme yapmaya olanak tanır. Bu sayede, bir işlemin sonucuna bağlı olarak başka bir işlem başlatabiliriz.
.then() Metodu: Promise başarıyla tamamlandığında çalışacak kodu tanımlar. Bir veya iki fonksiyon alabilir: `onFulfilled` ve `onRejected`. Genellikle sadece `onFulfilled` kullanılır ve hatalar için `.catch()` tercih edilir.
Kod:
function fetchData(url) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`[i]${url} adresinden veri çekiliyor...[/i]`);
resolve(`Veri çekildi: ${url}`);
}, 1500);
});
}
fetchData('https://api.example.com/users')
.then(userData => {
console.log(userData);
// İlk Promise'in sonucuna bağlı olarak yeni bir Promise döndürüyoruz
return fetchData('https://api.example.com/posts');
})
.then(postData => {
console.log(postData);
console.log('[b]Tüm veriler başarıyla alındı.[/b]');
})
.catch(error => {
console.error('[u]Zincirleme sırasında bir hata oluştu:[/u]', error);
});
.catch() Metodu: Promise zincirindeki herhangi bir noktada meydana gelen hataları yakalamak için kullanılır. En iyi uygulama, zincirin sonuna bir `.catch()` bloğu eklemektir. Bu, zincirdeki tüm Promise'lerden fırlatılan hataları tek bir noktada ele almanızı sağlar.
Kod:
function riskyOperation() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const errorOccurred = true; // Hata olduğunu simüle et
if (errorOccurred) {
reject(new Error('Riskli işlem başarısız oldu!'));
} else {
resolve('Riskli işlem başarılı oldu.');
}
}, 1000);
});
}
riskyOperation()
.then(result => {
console.log(result);
return 'İşlem devam ediyor...';
})
.then(nextStep => {
console.log(nextStep);
// Burada bir hata daha fırlatabiliriz
// throw new Error('Zincirde beklenmedik hata!');
})
.catch(err => {
console.error('[b]GENEL HATA YAKALANDI:[/b]', err.message);
})
.finally(() => {
console.log('Riskli işlem bloğu tamamlandı, hata olsa da olmasa da çalışırım.');
});
.finally() Metodu: Promise'in durumu ne olursa olsun (başarılı veya reddedilmiş) çalıştırılacak kodu tanımlar. Kaynakları serbest bırakma, yükleme göstergelerini kapatma gibi temizlik işlemleri için idealdir.
Gelişmiş Promise Metotları
Birden fazla Promise'i aynı anda yönetmek için Promise API, bir dizi statik metot sunar:
Promise.all(iterable): Bir dizi Promise'i alır ve tüm Promise'ler başarılı olduğunda çözülen tek bir Promise döndürür. Eğer herhangi bir Promise reddedilirse, `Promise.all` hemen reddedilir ve ilk reddedilen Promise'in hatasını döndürür.
Kod:
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3])
.then((values) => {
console.log('[b]Promise.all Başarılı:[/b]', values); // [3, 42, 'foo']
})
.catch((error) => {
console.error('[b]Promise.all Hata:[/b]', error);
});
// Eğer promise2 yerine Promise.reject(new Error('Hata')) olsaydı,
// Promise.all hemen reddedilirdi.
Promise.race(iterable): Bir dizi Promise alır ve bu Promise'lerden ilk tamamlanan (çözülen veya reddedilen) ile aynı değeri/hatayı döndürür. Yarışan Promise'lerden hangisi önce biterse, onun sonucunu döndürür.
Kod:
const promiseRace1 = new Promise((resolve) => setTimeout(resolve, 500, 'Birincil sunucu hazır.'));
const promiseRace2 = new Promise((resolve) => setTimeout(resolve, 100, 'Yedek sunucu daha hızlı.'));
Promise.race([promiseRace1, promiseRace2])
.then((value) => {
console.log('[b]Promise.race Kazanan:[/b]', value); // Çıktı: Yedek sunucu daha hızlı.
});
Promise.allSettled(iterable): (ES2020) Bir dizi Promise alır ve tüm Promise'ler çözüldüğünde (başarılı veya reddedilmiş olsalar bile) çözülen tek bir Promise döndürür. Sonuç, her Promise'in durumunu (status: 'fulfilled' veya 'rejected') ve değerini (value) veya hatasını (reason) içeren bir nesneler dizisidir.
Kod:
const promiseAS1 = Promise.resolve('Başarılı Veri');
const promiseAS2 = Promise.reject(new Error('Veri çekilemedi'));
Promise.allSettled([promiseAS1, promiseAS2])
.then((results) => {
console.log('[b]Promise.allSettled Sonuçları:[/b]', results);
/*
Çıktı:
[
{ status: 'fulfilled', value: 'Başarılı Veri' },
{ status: 'rejected', reason: Error: Veri çekilemedi }
]
*/
});
Promise.any(iterable): (ES2021) Bir dizi Promise alır ve Promise'lerden herhangi biri başarılı olduğunda çözülen tek bir Promise döndürür. Eğer tüm Promise'ler reddedilirse, bir `AggregateError` ile reddedilir.
Kod:
const promiseAny1 = new Promise((resolve, reject) => setTimeout(reject, 200, 'Sunucu 1 hatası.'));
const promiseAny2 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'Sunucu 2 başarılı.'));
const promiseAny3 = new Promise((resolve, reject) => setTimeout(resolve, 300, 'Sunucu 3 başarılı.'));
Promise.any([promiseAny1, promiseAny2, promiseAny3])
.then((value) => {
console.log('[b]Promise.any Kazanan:[/b]', value); // Çıktı: Sunucu 2 başarılı.
})
.catch((error) => {
console.error('[b]Promise.any Hata:[/b]', error);
});
// Eğer tüm Promise'ler reddedilirse, AggregateError fırlatılır.
// Promise.any([Promise.reject('a'), Promise.reject('b')]).catch(err => console.log(err.errors));
Hata Yönetimi ve En İyi Pratikler
"Asenkron programlamanın kalbinde, başarısızlıkları öngörmek ve bunlara uygun şekilde tepki vermektir." - Anonim
* Her Zaman Bir `.catch()` Ekleyin: Promise zincirinizin sonuna bir `.catch()` bloğu eklemek, beklenmeyen hataların uygulamanızı çökertmesini engeller ve hata ayıklamayı kolaylaştırır.
* Gereksiz İç İçe Promise'lerden Kaçının: Callback cehennemine benzer şekilde Promise cehennemine düşmemek için `.then()` zincirleme özelliğini etkin kullanın. Her `.then()` bloğundan yeni bir Promise döndürerek kodu düz tutun.
* Senkron Kodları Promise İçine Koymayın: Promise'ler asenkron işlemler içindir. Senkron kodları Promise yapısı içine koymak performansa fayda sağlamaz, aksine okunurluğu azaltır.
* Hataları Yeniden Fırlatın (Re-throw Errors): Bazen bir `.catch()` bloğunda hatayı işledikten sonra, hatanın zincirde yukarı doğru yayılmaya devam etmesini isteyebilirsiniz. Bu durumda, hatayı yeniden fırlatabilirsiniz (`throw error;`).
Async/Await ile Promise Kullanımı
ES2017 ile tanıtılan `async/await` sözdizimi, Promise'ler üzerinde daha okunabilir ve senkron görünümlü kod yazmamızı sağlar. Aslında `async/await`, Promise'ler üzerinde bir 'sentaktik şeker'dir ve altında yine Promise'leri kullanır. Bu sayede, uzun Promise zincirlerini daha lineer bir akışla yazabiliriz.
Kod:
async function fetchUserDataAndPosts() {
try {
const users = await fetchData('https://api.example.com/users-async'); // await, Promise çözülene kadar bekler
console.log(users);
const posts = await fetchData('https://api.example.com/posts-async');
console.log(posts);
console.log('[b]Async/Await ile tüm veriler başarıyla alındı.[/b]');
} catch (error) {
console.error('[u]Async/Await hatası:[/u]', error.message);
}
}
fetchUserDataAndPosts();
Sonuç

Resim: Promise akış şeması (temsili)
Promise API, JavaScript'teki asenkron programlamayı kökten değiştiren ve modern web geliştirmenin temel taşlarından biri haline gelen güçlü bir yapıdır. Callback hell sorununu çözmekle kalmayıp, kodu daha okunaklı, bakımı kolay ve hatalara karşı daha dirençli hale getirir. `Promise.all`, `Promise.race`, `Promise.allSettled` ve `Promise.any` gibi gelişmiş metotlar, karmaşık asenkron senaryoları yönetmek için esneklik sunar. `async/await` ile birleştiğinde ise, asenkron kod yazımı neredeyse senkron kod kadar anlaşılır hale gelir. Bu konuyu daha derinlemesine incelemek için MDN Promise belgesini ziyaret edebilirsiniz. Promise'lere hakim olmak, modern JavaScript geliştiricisinin olmazsa olmazıdır.
Umarım bu kapsamlı rehber, Promise API'nin gücünü ve kullanımını anlamanıza yardımcı olmuştur. İyi kodlamalar!