[NODE JS] Callback Hell ve Nasil Kacinilir?
Selam arkadaslar, bugun Node.js gelisitiricilerinin en cok sikayet ettigi konulardan biri olan callback hell'den bahsedecegiz. Benim ilk buyuk Node.js projem tam bir callback hell felaketiydi. Kodun sagina dogru kayiyordu, 8-10 seviye ic ice callback vardi ve kodu okuyan herkes "bu ne lan" diyordu. O gunlerden ogrendigim dersleri sizinle paylasacagim.
Callback Hell Nedir?
Callback hell, birbirine bagli asenkron islemlerin ic ice callback'ler ile yazilmasi sonucu olusan, okunmasi ve bakimi zor olan kod yapisina verilen isimdir. Kodun saga dogru kayarak bir piramit sekli almasina "pyramid of doom" da denir. Bir islemin sonucunu kullanarak baska bir islem yapmaniz gerektiginde, o islemin callback'i icinde baska bir asenkron islem cagirirsiniz ve bu boyle devam eder.
Bu sadece gorseli cirkin bir kod degil, ayni zamanda hata yonetimini zorlastiran, test edilmesi guc, bakimi pahali bir kod demektir. Her seviyede ayri ayri hata kontrolu yapmaniz gerekir ve bir hata oldugunda stack trace'i takip etmek kabus gibidir.
JavaScript:
const fs = require('fs');
const path = require('path');
// === CALLBACK HELL ORNEGI ===
// Boyle kod YAZMAYIN! Bu bir anti-pattern'dir.
// Kullanici bilgisini oku -> siparislerini bul -> urun detaylarini al -> rapor olustur
function raporOlusturKOTU(kullaniciId) {
fs.readFile('kullanicilar.json', 'utf8', (err1, kullaniciData) => {
if (err1) {
console.error('Kullanici dosyasi okunamadi:', err1.message);
return;
}
const kullanicilar = JSON.parse(kullaniciData);
const kullanici = kullanicilar.find(k => k.id === kullaniciId);
if (!kullanici) {
console.error('Kullanici bulunamadi');
return;
}
fs.readFile('siparisler.json', 'utf8', (err2, siparisData) => {
if (err2) {
console.error('Siparis dosyasi okunamadi:', err2.message);
return;
}
const siparisler = JSON.parse(siparisData);
const kullaniciSiparisleri = siparisler.filter(s => s.odpiojf === kullaniciId);
fs.readFile('urunler.json', 'utf8', (err3, urunData) => {
if (err3) {
console.error('Urun dosyasi okunamadi:', err3.message);
return;
}
const urunler = JSON.parse(urunData);
// Rapor olustur
const rapor = {
kullanici: kullanici.ad,
siparisler: kullaniciSiparisleri.map(s => {
const urun = urunler.find(u => u.id === s.urunId);
return { urun: urun ? urun.ad : 'Bilinmiyor', tutar: s.tutar };
})
};
const raporStr = JSON.stringify(rapor, null, 2);
fs.writeFile('rapor.json', raporStr, (err4) => {
if (err4) {
console.error('Rapor yazilamadi:', err4.message);
return;
}
console.log('Rapor olusturuldu!');
// Ve boyle devam eder...
});
});
});
});
}
Cozum 1: Fonksiyonlari Ayirmak
En basit cozum, her adimi ayri bir fonksiyon olarak tanimlamak ve callback'leri bu fonksiyonlara referans olarak gecirmektir. Bu yaklasim callback hell'i tamamen ortadan kaldirmaz ama kodu daha okunakli yapar. Her fonksiyon tek bir sorumluluga sahip olur ve test edilmesi de kolaylasir.
JavaScript:
const fs = require('fs');
// Fonksiyonlari ayirarak yazmak - daha temiz ama hala callback
function kullanicilariOku(callback) {
fs.readFile('kullanicilar.json', 'utf8', (err, data) => {
if (err) return callback(err);
try {
callback(null, JSON.parse(data));
} catch (parseErr) {
callback(parseErr);
}
});
}
function siparisleriOku(callback) {
fs.readFile('siparisler.json', 'utf8', (err, data) => {
if (err) return callback(err);
try {
callback(null, JSON.parse(data));
} catch (parseErr) {
callback(parseErr);
}
});
}
function urunleriOku(callback) {
fs.readFile('urunler.json', 'utf8', (err, data) => {
if (err) return callback(err);
try {
callback(null, JSON.parse(data));
} catch (parseErr) {
callback(parseErr);
}
});
}
function raporOlustur(kullaniciId, callback) {
kullanicilariOku((err, kullanicilar) => {
if (err) return callback(err);
const kullanici = kullanicilar.find(k => k.id === kullaniciId);
if (!kullanici) return callback(new Error('Kullanici bulunamadi'));
siparisleriOku((err, siparisler) => {
if (err) return callback(err);
urunleriOku((err, urunler) => {
if (err) return callback(err);
const rapor = { kullanici: kullanici.ad, toplam: 0 };
callback(null, rapor);
});
});
});
}
Cozum 2: Promise Kullanmak
En etkili cozum, callback'leri Promise'lere cevirmek ve async/await kullanmaktir. Node.js'in util.promisify fonksiyonu, error-first callback pattern'ine uyan fonksiyonlari otomatik olarak Promise donduren fonksiyonlara cevirir. Veya dogrudan fs/promises modulunu kullanabilirsiniz.
JavaScript:
const fs = require('fs/promises');
const { promisify } = require('util');
// Ayni islemi async/await ile yazalim - CENNETE HOSGELDINIZ
async function raporOlusturIYI(kullaniciId) {
try {
// Paralel olarak tum dosyalari oku
const [kullanicilarStr, siparislerStr, urunlerStr] = await Promise.all([
fs.readFile('kullanicilar.json', 'utf8'),
fs.readFile('siparisler.json', 'utf8'),
fs.readFile('urunler.json', 'utf8')
]);
const kullanicilar = JSON.parse(kullanicilarStr);
const siparisler = JSON.parse(siparislerStr);
const urunler = JSON.parse(urunlerStr);
// Kullaniciyi bul
const kullanici = kullanicilar.find(k => k.id === kullaniciId);
if (!kullanici) {
throw new Error(`Kullanici #${kullaniciId} bulunamadi`);
}
// Raporu olustur
const kullaniciSiparisleri = siparisler
.filter(s => s.kullaniciId === kullaniciId)
.map(s => {
const urun = urunler.find(u => u.id === s.urunId);
return {
urunAdi: urun ? urun.ad : 'Bilinmiyor',
tutar: s.tutar
};
});
const rapor = {
kullanici: kullanici.ad,
siparisler: kullaniciSiparisleri,
toplam: kullaniciSiparisleri.reduce((t, s) => t + s.tutar, 0),
olusturulma: new Date().toISOString()
};
// Raporu yaz
await fs.writeFile('rapor.json', JSON.stringify(rapor, null, 2));
console.log('Rapor basariyla olusturuldu!');
return rapor;
} catch (error) {
console.error('Rapor olusturma hatasi:', error.message);
throw error;
}
}
// Eski callback fonksiyonlarini promisify ile cevirme
const eski = require('fs');
const readFileAsync = promisify(eski.readFile);
const writeFileAsync = promisify(eski.writeFile);
// Artik promise olarak kullanilabilir
async function ornekKullanim() {
const data = await readFileAsync('config.json', 'utf8');
console.log('Okunan veri:', data);
}
// Callback hell'den kurtulmak icin ozet:
// 1. fs/promises kullanin (veya util.promisify)
// 2. async/await ile yazin
// 3. Bagimsiz islemleri Promise.all ile paralel calistirin
// 4. Hata yonetimini try/catch ile yapin
// 5. Her fonksiyon tek bir is yapsin
Sonuc
Callback hell gercek bir sorun ama cozumu de cok basit. Modern JavaScript'in sundugu Promise ve async/await yapilarini kullanarak bu sorundan tamamen kurtulabilirsiniz. Benim tavsiyem, eger 2024'te yeni bir proje basliyorsaniz, callback'leri tamamen terk edin ve async/await ile yazin. Eski projelerde ise yavastan refactor yapin, yeni eklediginiz kodlari async/await ile yazin ve zaman buldukca eski callback'leri donusturun.
Ha bu arada, bazi insanlar "callback hell degil, callback'lerin ne yaptigini anlamama problemi" diye tartisir. Bir yere kadar haklilardır ama yine de 10 seviye ic ice callback yazmak iyi bir pratik degildir. Neyse, arkadaslar bir sonraki konuda Promise'lerin kendisini detayli inceleyecegiz. Gorusuruz!
Emeğiniz ve katkınız için teşekkür ederiz!
Konunuz gerekli incelemelerden geçerek onaylandı. Topluluğumuza katkılarınızın devamını bekliyoruz.
İyi forumlar!
Konunuz gerekli incelemelerden geçerek onaylandı. Topluluğumuza katkılarınızın devamını bekliyoruz.
İyi forumlar!

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)
Benzer konular
- Cevaplar
- 9
- Görüntüleme
- 233
- Cevaplar
- 0
- Görüntüleme
- 52
- Cevaplar
- 1
- Görüntüleme
- 522
- Cevaplar
- 5
- Görüntüleme
- 3K