- Senkron ve asenkron kod arasındaki farkı ve
setTimeout'un çalışmasını açıklamak - Promise oluşturmak ve sonucunu
then,catchvefinallyile işlemek async/awaitvetry...catchile okunaklı asenkron kod yazmakPromise.allile birkaç işi paralel yürütmek
Bir kafede garson siparişini mutfağa iletir, ama pilav pişene kadar mutfağın kapısında beklemez; başka masalara servis yapar. Yemek hazır olunca da getirir. JavaScript de aynı şekilde çalışır: sunucu yanıtı, zamanlayıcı ya da dosya gibi uzun süren işleri beklerken sayfayı dondurmaz, başka işlerle ilgilenir. Buna asenkron programlama denir.
Senkron ve asenkron
Şimdiye kadar yazdığımız kod senkrondu: her komut bir öncekinin bitmesini bekler. setTimeout(fonksiyon, ms) ise fonksiyonu belirtilen milisaniye sonra çalışmak üzere “bir kenara koyar” ve program hemen bir sonraki satıra geçer. Çıktının sırasına dikkat et:
console.log('1. Order placed');
setTimeout(() => {
console.log('4. Plov is ready');
}, 1000);
setTimeout(() => {
console.log('3. Tea is ready');
}, 0);
console.log('2. Waiter serves another table');▸ Beklenen çıktı
1. Order placed 2. Waiter serves another table 3. Tea is ready 4. Plov is ready
JavaScript aynı anda yalnızca tek bir iş yapar. Hazır olan callback'ler bir kuyrukta bekler; olay döngüsü, o anki senkron kod bitince onları kuyruktan tek tek alıp çalıştırır. Bu yüzden 0 ms gecikmeli bir setTimeout bile tüm senkron koddan sonra çalışır.
Callback'ler ve sorunları
Asenkron iş bittiğinde sonucu almanın en eski yolu bir callback vermektir: “hazır olunca bu fonksiyonu çağır.” Birkaç iş art arda yapılacaksa callback'ler iç içe girer ve kod sağa doğru merdiven gibi uzar. Geliştiriciler buna callback hell (callback cehennemi) der.
function cook(dish, callback) {
setTimeout(() => callback(`${dish} is ready`), 300);
}
cook('Plov', (first) => {
console.log(first);
cook('Dolma', (second) => {
console.log(second);
cook('Pakhlava', (third) => {
console.log(third);
});
});
});▸ Beklenen çıktı
Plov is ready Dolma is ready Pakhlava is ready
Promise
Promise (söz), gelecekte hazır olacak bir sonucu temsil eden bir nesnedir. Üç durumu vardır: pending (beklemede), fulfilled (başarıyla tamamlandı) ve rejected (hatayla bitti). Promise oluştururken iki fonksiyon alırız: başarı için resolve, hata için reject. Sonucu then ile, hatayı catch ile alırız; finally ise her durumda çalışır.
function cook(dish, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (!dish) {
reject(new Error('No dish ordered'));
} else {
resolve(`${dish} is ready`);
}
}, time);
});
}
cook('Plov', 300)
.then((message) => {
console.log(message);
return cook('Dolma', 200);
})
.then((message) => console.log(message))
.then(() => cook('', 100))
.catch((error) => console.log('Error:', error.message))
.finally(() => console.log('Kitchen closed'));▸ Beklenen çıktı
Plov is ready Dolma is ready Error: No dish ordered Kitchen closed
then bir sonraki adımı döndürür, tüm hatalar ise tek bir catch'te yakalanır.async ve await
async/await, Promise tabanlı kodu sıradan senkron kod gibi yazmanı sağlar. async ile tanımlanan bir fonksiyon her zaman Promise döndürür. İçinde await, programın geri kalanını dondurmadan bir Promise'in sonucunu bekler. Hatalar ise tanıdık try...catch ile yakalanır.
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function cook(dish, time) {
await wait(time);
if (!dish) throw new Error('No dish ordered');
return `${dish} is ready`;
}
async function dinner() {
try {
console.log(await cook('Plov', 300));
console.log(await cook('Dolma', 200));
console.log(await cook('', 100));
} catch (error) {
console.log('Error:', error.message);
} finally {
console.log('Kitchen closed');
}
}
dinner();▸ Beklenen çıktı
Plov is ready Dolma is ready Error: No dish ordered Kitchen closed
async function getScore() {
return 42;
}
const result = getScore();
console.log(result instanceof Promise);
result.then((score) => console.log('Score:', score));▸ Beklenen çıktı
true Score: 42
Paralel işler ve fetch
İşler birbirine bağlı değilse onları sırayla beklemek zaman kaybıdır. Promise.all tüm Promise'leri aynı anda başlatır ve hepsi hazır olunca sonuçları aynı sırayla bir dizi olarak döndürür. Aşağıdaki üç iş art arda 600 ms sürerdi, paralel olarak ise yaklaşık 300 ms sürer. Tek bir Promise bile başarısız olursa Promise.all da hatayla biter.
const cook = (dish, ms) =>
new Promise((resolve) => setTimeout(() => resolve(`${dish} is ready`), ms));
async function breakfast() {
const results = await Promise.all([
cook('Tea', 300),
cook('Bread', 100),
cook('Jam', 200),
]);
console.log(results);
}
breakfast();▸ Beklenen çıktı
[ 'Tea is ready', 'Bread is ready', 'Jam is ready' ]
Gerçek projelerde en sık karşılaşacağın asenkron işlem, bir sunucudan fetch ile veri yüklemektir. fetch bir Promise döndürür; yanıt gelince response.json() onu bir JavaScript nesnesine çevirir, bu da bir Promise'tir. Aşağıdaki örnek bir şablondur: adres yalnızca temsilîdir.
async function loadWeather(city) {
try {
const response = await fetch(`https://api.example.com/weather?city=${city}`);
if (!response.ok) {
throw new Error(`Server answered ${response.status}`);
}
const data = await response.json();
console.log(`${city}: ${data.temperature} °C`);
} catch (error) {
console.log('Could not load the weather:', error.message);
}
}
loadWeather('Baku');| Yöntem | Avantajı | Dezavantajı |
|---|---|---|
| Callback | basittir, her yerde çalışır | iç içe girince okunmaz |
| Promise | zincirler, tüm hatalar için tek catch | uzun zincirler yine zor okunur |
| async/await | normal kod gibi okunur, try...catch | await'i unutmak kolaydır |
countdown(from) adlı asenkron fonksiyonu yaz: from değerinden 1'e kadar sayıları yazdırsın, her sayıdan sonra wait(300) ile 300 ms beklesin ve en sonda Go! yazdırsın. Son satırdaki await, burada bir modülün en üst düzeyindeki gibi çalışır.
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function countdown(from) {
// print from, from - 1, ..., 1 with a 300 ms pause after each number
// then print Go!
}
await countdown(3);▸ Beklenen çıktı
3 2 1 Go!
Önemli noktalar
- Asenkron kod beklerken programı dondurmaz;
setTimeoutbir fonksiyonu sonraya planlar. - Hazır callback'ler yalnızca tüm senkron kod bittikten sonra çalışır (olay döngüsü).
- Promise'in üç durumu vardır: pending, fulfilled, rejected; sonuç
then, hatacatchile alınır. asyncfonksiyon her zaman Promise döndürür;awaitsonucu bekler, hatalartry...catchile yakalanır.Promise.allbağımsız işleri paralel yürütür ve sonuçları aynı sırayla döndürür.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.
console.log('A'); setTimeout(() => console.log('B'), 0); console.log('C'); hangi sırayla yazdırır?