Asenkron Programlama Nedir?
Asenkron programlama, kodun sıralı çalışmasını beklemeden diğer işlemlerin devam edebilmesini sağlar. Web geliştirmede özellikle API çağrıları, dosya okuma/yazma ve zamanlayıcılar için kritiktir.
Senkron vs Asenkron
// Senkron kod - her satır sırayla çalışır
console.log('1. İlk işlem');
console.log('2. İkinci işlem');
console.log('3. Üçüncü işlem');
// Çıktı: 1, 2, 3 (sırayla)
// Asenkron kod - bazı işlemler bekletilir
console.log('1. İlk işlem');
setTimeout(() => {
console.log('2. Gecikmeli işlem');
}, 1000);
console.log('3. Üçüncü işlem');
// Çıktı: 1, 3, 2 (1 saniye sonra)
Callback Fonksiyonlar
Temel Callback Kullanımı
// Basit callback örneği
function islemTamamlaninca(sonuc) {
console.log('İşlem tamamlandı:', sonuc);
}
function uzunIslem(callback) {
console.log('İşlem başladı...');
setTimeout(() => {
let sonuc = 'Başarılı!';
callback(sonuc);
}, 2000);
}
uzunIslem(islemTamamlaninca);
// Anonim callback
uzunIslem(function(sonuc) {
console.log('Anonim callback:', sonuc);
});
// Arrow function callback
uzunIslem(sonuc => console.log('Arrow callback:', sonuc));
Callback Hell (Callback Cehennemi)
// İç içe callback'ler - kaçınılması gereken durum
function kullaniciBilgisiAl(id, callback) {
setTimeout(() => {
callback({id, isim: 'Furkan', departmanId: 5});
}, 1000);
}
function departmanBilgisiAl(departmanId, callback) {
setTimeout(() => {
callback({id: departmanId, isim: 'IT Departmanı'});
}, 1000);
}
function projeListesiAl(departmanId, callback) {
setTimeout(() => {
callback(['Proje A', 'Proje B', 'Proje C']);
}, 1000);
}
// Callback hell - okunması zor!
kullaniciBilgisiAl(123, function(kullanici) {
departmanBilgisiAl(kullanici.departmanId, function(departman) {
projeListesiAl(departman.id, function(projeler) {
console.log(`${kullanici.isim} (${departman.isim}) - Projeler:`, projeler);
});
});
});
Promise (Söz) Kavramı
Promise Temelleri
// Promise oluşturma
let basitPromise = new Promise((resolve, reject) => {
let basarili = Math.random() > 0.5;
setTimeout(() => {
if (basarili) {
resolve('İşlem başarılı!');
} else {
reject('İşlem başarısız!');
}
}, 1000);
});
// Promise kullanma
basitPromise
.then(sonuc => {
console.log('Başarı:', sonuc);
})
.catch(hata => {
console.log('Hata:', hata);
})
.finally(() => {
console.log('İşlem tamamlandı (her durumda çalışır)');
});
Promise States (Durumları)
// Pending (Bekliyor)
let bekleyenPromise = new Promise((resolve, reject) => {
// Henüz tamamlanmadı
});
// Fulfilled (Başarılı)
let basariliPromise = Promise.resolve('Başarılı sonuç');
// Rejected (Başarısız)
let basarisizPromise = Promise.reject('Hata mesajı');
// Durum kontrolü
console.log(basariliPromise); // Promise {<fulfilled>: "Başarılı sonuç"}
Promise Chain (Zincir)
function kullaniciBilgisiAl(id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({id, isim: 'Furkan', departmanId: 5});
}, 1000);
});
}
function departmanBilgisiAl(departmanId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({id: departmanId, isim: 'IT Departmanı'});
}, 1000);
});
}
function projeListesiAl(departmanId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(['Proje A', 'Proje B', 'Proje C']);
}, 1000);
});
}
// Promise chain - çok daha temiz!
kullaniciBilgisiAl(123)
.then(kullanici => {
console.log('Kullanıcı:', kullanici);
return departmanBilgisiAl(kullanici.departmanId);
})
.then(departman => {
console.log('Departman:', departman);
return projeListesiAl(departman.id);
})
.then(projeler => {
console.log('Projeler:', projeler);
})
.catch(hata => {
console.error('Hata oluştu:', hata);
});
Promise.all ve Promise.race
// Paralel işlemler - hepsi tamamlanana kadar bekle
let promise1 = fetch('/api/kullanicilar');
let promise2 = fetch('/api/departmanlar');
let promise3 = fetch('/api/projeler');
Promise.all([promise1, promise2, promise3])
.then(responses => {
console.log('Tüm API çağrıları tamamlandı:', responses);
// Hepsi başarılı olursa çalışır
})
.catch(hata => {
console.log('En az bir API çağrısı başarısız:', hata);
// Herhangi biri başarısız olursa çalışır
});
// İlk tamamlanan için bekle
Promise.race([promise1, promise2, promise3])
.then(response => {
console.log('İlk tamamlanan:', response);
});
// Promise.allSettled - hepsinin sonucunu bekle (başarısız olanlar dahil)
Promise.allSettled([promise1, promise2, promise3])
.then(sonuclar => {
sonuclar.forEach((sonuc, indeks) => {
if (sonuc.status === 'fulfilled') {
console.log(`Promise ${indeks} başarılı:`, sonuc.value);
} else {
console.log(`Promise ${indeks} başarısız:`, sonuc.reason);
}
});
});
async/await (Modern Asenkron Kod)
Temel async/await Kullanımı
// async function tanımlama
async function veriAl() {
try {
let kullanici = await kullaniciBilgisiAl(123);
let departman = await departmanBilgisiAl(kullanici.departmanId);
let projeler = await projeListesiAl(departman.id);
console.log('Kullanıcı:', kullanici);
console.log('Departman:', departman);
console.log('Projeler:', projeler);
return {kullanici, departman, projeler};
} catch (hata) {
console.error('Veri alırken hata:', hata);
throw hata;
}
}
// async function çağırma
veriAl()
.then(sonuc => console.log('Tüm veriler:', sonuc))
.catch(hata => console.error('Ana hata:', hata));
Paralel await İşlemleri
async function parallelVeriAl() {
try {
// Sıralı - yavaş (3 saniye)
let kullanici = await kullaniciBilgisiAl(123);
let ayarlar = await ayarlarAl(123);
let bildirimler = await bildirimlerAl(123);
// Paralel - hızlı (1 saniye)
let [kullanici2, ayarlar2, bildirimler2] = await Promise.all([
kullaniciBilgisiAl(123),
ayarlarAl(123),
bildirimlerAl(123)
]);
return {kullanici: kullanici2, ayarlar: ayarlar2, bildirimler: bildirimler2};
} catch (hata) {
console.error('Paralel veri alma hatası:', hata);
}
}
Error Handling with async/await
async function guvenliVeriAl(id) {
try {
let kullanici = await kullaniciBilgisiAl(id);
if (!kullanici) {
throw new Error('Kullanıcı bulunamadı');
}
let departman = await departmanBilgisiAl(kullanici.departmanId);
return {kullanici, departman};
} catch (hata) {
if (hata.message === 'Kullanıcı bulunamadı') {
console.log('Özel hata işleme:', hata.message);
return null;
}
// Beklenmedik hatalar
console.error('Beklenmedik hata:', hata);
throw hata; // Üst seviyeye ilet
}
}
// Kullanım
async function kullan() {
let sonuc = await guvenliVeriAl(999);
if (sonuc) {
console.log('Veri alındı:', sonuc);
} else {
console.log('Veri alınamadı');
}
}
Fetch API (HTTP İstekleri)
Temel Fetch Kullanımı
// GET isteği
async function kullaniciListesiAl() {
try {
let response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
let kullanicilar = await response.json();
console.log('Kullanıcılar:', kullanicilar);
return kullanicilar;
} catch (hata) {
console.error('Kullanıcı listesi alırken hata:', hata);
}
}
// POST isteği
async function kullaniciOlustur(kullaniciVerisi) {
try {
let response = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(kullaniciVerisi)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
let yeniKullanici = await response.json();
console.log('Yeni kullanıcı oluşturuldu:', yeniKullanici);
return yeniKullanici;
} catch (hata) {
console.error('Kullanıcı oluştururken hata:', hata);
}
}
// Kullanım
kullaniciOlustur({
name: 'Furkan Çelik',
email: '[email protected]',
phone: '0555-123-4567'
});
Gelişmiş Fetch Konfigürasyonu
// Timeout ile
async function timeoutFetch(url, options = {}, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
clearTimeout(timeoutId);
return response;
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('Request timeout');
}
throw error;
}
}
// Auth header ile
async function authFetch(url, token, options = {}) {
return fetch(url, {
...options,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
...options.headers
}
});
}
// Retry mekanizması ile
async function retryFetch(url, options = {}, maxRetries = 3) {
for (let i = 0; i <= maxRetries; i++) {
try {
const response = await fetch(url, options);
if (response.ok) return response;
if (i === maxRetries) {
throw new Error(`Failed after ${maxRetries} retries`);
}
} catch (error) {
if (i === maxRetries) throw error;
// Exponential backoff
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
}
}
Modern JavaScript Özellikleri
Template Literals ve Tagged Templates
// Temel template literal
let isim = 'Furkan';
let yas = 25;
let mesaj = `Merhaba, ben ${isim} ve ${yas} yaşındayım.`;
// Çok satırlı
let htmlSablon = `
<div class="card">
<h3>${isim}</h3>
<p>Yaş: ${yas}</p>
<p>Durum: ${yas >= 18 ? 'Yetişkin' : 'Çocuk'}</p>
</div>
`;
// Tagged templates
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
const value = values[i] ? `<strong>${values[i]}</strong>` : '';
return result + string + value;
}, '');
}
let vurguluMesaj = highlight`Merhaba ${isim}, yaşın ${yas}`;
console.log(vurguluMesaj); // Merhaba <strong>Furkan</strong>, yaşın <strong>25</strong>
Destructuring Assignment
// Array destructuring
let renkler = ['kırmızı', 'yeşil', 'mavi', 'sarı'];
let [ilk, ikinci, ...geriKalan] = renkler;
console.log(ilk); // 'kırmızı'
console.log(geriKalan); // ['mavi', 'sarı']
// Object destructuring
let kullanici = {
isim: 'Furkan',
yas: 25,
email: '[email protected]',
adres: {
sehir: 'İstanbul',
ilce: 'Kadıköy'
}
};
let {isim, yas, email, adres: {sehir}} = kullanici;
console.log(isim, yas, sehir); // 'Furkan' 25 'İstanbul'
// Varsayılan değerler
let {telefon = 'Belirtilmemiş'} = kullanici;
console.log(telefon); // 'Belirtilmemiş'
// Yeniden adlandırma
let {isim: kullaniciAdi, yas: kullaniciYasi} = kullanici;
console.log(kullaniciAdi); // 'Furkan'
Spread ve Rest Operatörleri
// Spread operatörü (...)
let sayilar1 = [1, 2, 3];
let sayilar2 = [4, 5, 6];
let birlesik = [...sayilar1, ...sayilar2]; // [1, 2, 3, 4, 5, 6]
// Obje spread
let temelBilgi = {isim: 'Furkan', yas: 25};
let tamBilgi = {...temelBilgi, email: '[email protected]', sehir: 'İstanbul'};
// Function arguments
function toplam(...sayilar) {
return sayilar.reduce((sum, num) => sum + num, 0);
}
console.log(toplam(1, 2, 3, 4, 5)); // 15
// Rest in destructuring
let [birinci, ...kalanlar] = [1, 2, 3, 4, 5];
console.log(birinci); // 1
console.log(kalanlar); // [2, 3, 4, 5]
Arrow Functions ve Lexical This
// Traditional function vs Arrow function
let obje = {
isim: 'Test',
// Traditional - kendi this'i var
geleneksel: function() {
console.log('Geleneksel:', this.isim); // 'Test'
setTimeout(function() {
console.log('Timeout geleneksel:', this.isim); // undefined (window.isim)
}, 1000);
},
// Arrow - üst scope'un this'ini kullanır
arrow: function() {
console.log('Arrow dış:', this.isim); // 'Test'
setTimeout(() => {
console.log('Timeout arrow:', this.isim); // 'Test'
}, 1000);
}
};
obje.geleneksel();
obje.arrow();
Pratik Örnekler
Hava Durumu Uygulaması
class HavaDurumuApp {
constructor() {
this.apiKey = 'YOUR_API_KEY'; // OpenWeatherMap API key
this.baseUrl = 'https://api.openweathermap.org/data/2.5';
this.init();
}
init() {
this.createHTML();
this.bindEvents();
}
createHTML() {
document.body.innerHTML = `
<div class="weather-app">
<h1>Hava Durumu</h1>
<div class="search-container">
<input type="text" id="cityInput" placeholder="Şehir adı girin...">
<button id="searchBtn">Ara</button>
<button id="locationBtn">Konumum</button>
</div>
<div id="loading" class="loading hidden">Yükleniyor...</div>
<div id="weatherInfo" class="weather-info hidden"></div>
<div id="forecast" class="forecast hidden"></div>
<div id="error" class="error hidden"></div>
</div>
`;
this.addStyles();
}
addStyles() {
let style = document.createElement('style');
style.textContent = `
.weather-app { max-width: 600px; margin: 50px auto; padding: 20px; }
.search-container { display: flex; gap: 10px; margin-bottom: 20px; }
#cityInput { flex: 1; padding: 10px; }
button { padding: 10px 15px; cursor: pointer; }
.weather-info { background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0; }
.forecast { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; }
.forecast-item { background: #e9ecef; padding: 15px; border-radius: 5px; text-align: center; }
.hidden { display: none; }
.loading { text-align: center; padding: 20px; }
.error { background: #f8d7da; color: #721c24; padding: 15px; border-radius: 5px; }
`;
document.head.appendChild(style);
}
bindEvents() {
let searchBtn = document.getElementById('searchBtn');
let locationBtn = document.getElementById('locationBtn');
let cityInput = document.getElementById('cityInput');
searchBtn.addEventListener('click', () => this.searchByCity());
locationBtn.addEventListener('click', () => this.searchByLocation());
cityInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') this.searchByCity();
});
}
async searchByCity() {
let city = document.getElementById('cityInput').value.trim();
if (!city) return;
try {
this.showLoading();
let weatherData = await this.getWeatherByCity(city);
let forecastData = await this.getForecastByCity(city);
this.displayWeather(weatherData, forecastData);
} catch (error) {
this.showError('Şehir bulunamadı: ' + error.message);
}
}
async searchByLocation() {
try {
this.showLoading();
let position = await this.getCurrentPosition();
let {latitude, longitude} = position.coords;
let weatherData = await this.getWeatherByCoords(latitude, longitude);
let forecastData = await this.getForecastByCoords(latitude, longitude);
this.displayWeather(weatherData, forecastData);
} catch (error) {
this.showError('Konum alınamadı: ' + error.message);
}
}
getCurrentPosition() {
return new Promise((resolve, reject) => {
if (!navigator.geolocation) {
reject(new Error('Geolocation desteklenmiyor'));
return;
}
navigator.geolocation.getCurrentPosition(resolve, reject);
});
}
async getWeatherByCity(city) {
let response = await fetch(
`${this.baseUrl}/weather?q=${city}&appid=${this.apiKey}&units=metric&lang=tr`
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
}
async getForecastByCity(city) {
let response = await fetch(
`${this.baseUrl}/forecast?q=${city}&appid=${this.apiKey}&units=metric&lang=tr`
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
}
async getWeatherByCoords(lat, lon) {
let response = await fetch(
`${this.baseUrl}/weather?lat=${lat}&lon=${lon}&appid=${this.apiKey}&units=metric&lang=tr`
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
}
async getForecastByCoords(lat, lon) {
let response = await fetch(
`${this.baseUrl}/forecast?lat=${lat}&lon=${lon}&appid=${this.apiKey}&units=metric&lang=tr`
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
}
displayWeather(current, forecast) {
this.hideLoading();
// Güncel hava durumu
let weatherInfo = document.getElementById('weatherInfo');
weatherInfo.innerHTML = `
<h2>${current.name}, ${current.sys.country}</h2>
<div style="display: flex; align-items: center; gap: 20px;">
<img src="https://openweathermap.org/img/w/${current.weather[0].icon}.png" alt="${current.weather[0].description}">
<div>
<div style="font-size: 2em; font-weight: bold;">${Math.round(current.main.temp)}°C</div>
<div>${current.weather[0].description}</div>
</div>
</div>
<div style="margin-top: 15px; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;">
<div>Hissedilen: ${Math.round(current.main.feels_like)}°C</div>
<div>Nem: ${current.main.humidity}%</div>
<div>Rüzgar: ${current.wind.speed} m/s</div>
<div>Basınç: ${current.main.pressure} hPa</div>
</div>
`;
weatherInfo.classList.remove('hidden');
// 5 günlük tahmin
let forecastContainer = document.getElementById('forecast');
let dailyForecasts = this.processForecastData(forecast);
forecastContainer.innerHTML = dailyForecasts.map(day => `
<div class="forecast-item">
<div style="font-weight: bold;">${day.date}</div>
<img src="https://openweathermap.org/img/w/${day.icon}.png" alt="${day.description}">
<div>${Math.round(day.temp)}°C</div>
<div style="font-size: 0.9em;">${day.description}</div>
</div>
`).join('');
forecastContainer.classList.remove('hidden');
}
processForecastData(forecast) {
let dailyData = {};
forecast.list.forEach(item => {
let date = new Date(item.dt * 1000);
let day = date.toLocaleDateString('tr-TR', {weekday: 'short', day: 'numeric'});
if (!dailyData[day]) {
dailyData[day] = {
date: day,
temp: item.main.temp,
icon: item.weather[0].icon,
description: item.weather[0].description
};
}
});
return Object.values(dailyData).slice(0, 5);
}
showLoading() {
document.getElementById('loading').classList.remove('hidden');
document.getElementById('weatherInfo').classList.add('hidden');
document.getElementById('forecast').classList.add('hidden');
document.getElementById('error').classList.add('hidden');
}
hideLoading() {
document.getElementById('loading').classList.add('hidden');
}
showError(message) {
this.hideLoading();
let errorDiv = document.getElementById('error');
errorDiv.textContent = message;
errorDiv.classList.remove('hidden');
document.getElementById('weatherInfo').classList.add('hidden');
document.getElementById('forecast').classList.add('hidden');
}
}
// Demo için mock versiyon (API key gerekmez)
class MockHavaDurumuApp extends HavaDurumuApp {
async getWeatherByCity(city) {
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 1000));
return {
name: city,
sys: {country: 'TR'},
main: {
temp: 22,
feels_like: 24,
humidity: 65,
pressure: 1013
},
weather: [{
icon: '01d',
description: 'açık hava'
}],
wind: {speed: 3.2}
};
}
async getForecastByCity(city) {
await new Promise(resolve => setTimeout(resolve, 500));
return {
list: Array.from({length: 40}, (_, i) => ({
dt: Date.now() / 1000 + (i * 3 * 60 * 60), // 3 hour intervals
main: {temp: 20 + Math.random() * 10},
weather: [{
icon: '01d',
description: 'güneşli'
}]
}))
};
}
}
// Uygulamayı başlat
new MockHavaDurumuApp();
Pratik Egzersizler
Egzersiz 1: RESTful API Client
class ApiClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
// Tüm HTTP metodları için generic fetch wrapper
async request(endpoint, options = {}) {
// 1. URL oluşturma
// 2. Headers yönetimi
// 3. Error handling
// 4. Response parsing
// 5. Retry logic
}
// CRUD operasyonları
async get(endpoint) { /* GET isteği */ }
async post(endpoint, data) { /* POST isteği */ }
async put(endpoint, data) { /* PUT isteği */ }
async delete(endpoint) { /* DELETE isteği */ }
}
Egzersiz 2: Real-time Chat Simulator
class ChatApp {
constructor() {
this.messages = [];
this.users = [];
this.currentUser = null;
}
// WebSocket simulation with async/await
async sendMessage(message) {
// 1. Message validation
// 2. Async send simulation
// 3. Real-time update
// 4. Error handling
}
async receiveMessages() {
// 1. Long polling simulation
// 2. Message parsing
// 3. UI update
}
}
Egzersiz 3: Progressive Image Loader
class ImageLoader {
constructor() {
this.loadQueue = [];
this.loading = false;
}
async loadImage(src) {
// 1. Progressive loading
// 2. Lazy loading
// 3. Placeholder handling
// 4. Error recovery
}
async loadImagesInBatches(imageUrls, batchSize = 3) {
// 1. Batch processing
// 2. Concurrency control
// 3. Progress tracking
}
}
Ipucu
Best Practice: async/await kullanırken her zaman try-catch ile error handling yapın. Promise.all kullanırken biri başarısız olursa hepsinin başarısız olacağını unutmayın.
Seri Tamamlandı! 🎉
Tebrikler! JavaScript eğitim serisini tamamladınız. Artık modern JavaScript ile:
Öğrendikleriniz:
- ✅ Asenkron programlama temelleri
- ✅ Promise ve async/await kullanımı
- ✅ Fetch API ile HTTP istekleri
- ✅ Modern JavaScript özellikleri (ES6+)
- ✅ Error handling ve best practices
- ✅ Gerçek dünya projeleri geliştirme
Sıradaki Adımlar:
- Framework’ler: React, Vue.js veya Angular öğrenin
- Node.js: Backend geliştirme için JavaScript
- TypeScript: Tip güvenliği için
- Build Tools: Webpack, Vite, Parcel
- Testing: Jest, Cypress ile test yazma
JavaScript yolculuğunuz daha yeni başlıyor! Pratik yapın, projeler geliştirin ve JavaScript ekosistemini keşfetmeye devam edin. 🚀