Türkiye'de ilk Mobil & PC Aynı Anda Metin2 Oyna. Triarchonline kalıcı ve uzun ömürlü yapısı ile 24 Temmuz'da açılıyor. | 1-99 Mobil Metin2 Triarch HEMEN TIKLA!
[NODE JS] Kendi EventEmitter Sinifnizi Yazin
Merhaba arkadaslar, bugun bir onceki konuyu bir adim ileriye tasiyacagiz: kendi EventEmitter sinifimizi sifirdan yazacagiz. Bu hem EventEmitter'in nasil calistigini derinlemesine anlamanizi saglayacak hem de JavaScript'teki Observer pattern'ini ogrenmis olacaksiniz. Ben mulakatlarda bu soruyla karsilastim ve isine yaramisti.
Neden Kendi EventEmitter'imizi Yazalim?
Pratikte Node.js'in yerlesik EventEmitter'ini kullanacaksiniz ama kendi implementasyonunuzu yazmak iki acidan faydalidir. Birincisi, kaputun altinda ne oldugunu anlarsiniz ve sorun cikardiginda daha hizli debug edersiniz. Ikincisi, ozel ihtiyaclariniz icin ozellestirilmis bir event sistemi yazabilirsiniz. Mesela typed events, wildcard listeners veya async event handling gibi ozellikler ekleyebilirsiniz.
Aslinda EventEmitter cok karmasik bir yapi degil. Temelinde bir nesne var, bu nesnenin icinde event isimlerini key, listener dizilerini value olarak tutan bir Map veya object var. on ile listener eklenir, emit ile tetiklenir, off ile kaldirilir. Iste bu kadar.
JavaScript:
// Kendi EventEmitter implementasyonumuz
class BenimEventEmitter {
constructor() {
this._events = {};
this._maxListeners = 10;
}
// Dinleyici ekle
on(eventAdi, dinleyici) {
if (typeof dinleyici !== 'function') {
throw new TypeError('Dinleyici bir fonksiyon olmalidir');
}
if (!this._events[eventAdi]) {
this._events[eventAdi] = [];
}
// Max listener uyarisi
if (this._events[eventAdi].length >= this._maxListeners) {
console.warn(
`Uyari: "${eventAdi}" eventi icin ${this._maxListeners} dinleyici limitine ulasildi. ` +
'Memory leak olabilir.'
);
}
this._events[eventAdi].push({ fn: dinleyici, once: false });
return this; // Chaining icin
}
// Tek seferlik dinleyici
once(eventAdi, dinleyici) {
if (typeof dinleyici !== 'function') {
throw new TypeError('Dinleyici bir fonksiyon olmalidir');
}
if (!this._events[eventAdi]) {
this._events[eventAdi] = [];
}
this._events[eventAdi].push({ fn: dinleyici, once: true });
return this;
}
// Olay yayinla
emit(eventAdi, ...args) {
const dinleyiciler = this._events[eventAdi];
if (!dinleyiciler || dinleyiciler.length === 0) {
// 'error' eventi icin ozel davranis
if (eventAdi === 'error') {
const hata = args[0];
if (hata instanceof Error) {
throw hata;
}
throw new Error('Yakalanmamis error eventi');
}
return false;
}
// Dinleyicilerin kopyasini al (dongu sirasinda degisebilir)
const kopyaDinleyiciler = [...dinleyiciler];
for (const dinleyici of kopyaDinleyiciler) {
dinleyici.fn.apply(this, args);
// once dinleyicisini kaldir
if (dinleyici.once) {
this.off(eventAdi, dinleyici.fn);
}
}
return true;
}
// Dinleyici kaldir
off(eventAdi, dinleyici) {
const dinleyiciler = this._events[eventAdi];
if (!dinleyiciler) return this;
this._events[eventAdi] = dinleyiciler.filter(d => d.fn !== dinleyici);
// Bos dizi kalirsa temizle
if (this._events[eventAdi].length === 0) {
delete this._events[eventAdi];
}
return this;
}
// Tum dinleyicileri kaldir
removeAllListeners(eventAdi) {
if (eventAdi) {
delete this._events[eventAdi];
} else {
this._events = {};
}
return this;
}
// Dinleyici sayisi
listenerCount(eventAdi) {
const dinleyiciler = this._events[eventAdi];
return dinleyiciler ? dinleyiciler.length : 0;
}
// Event isimlerini listele
eventNames() {
return Object.keys(this._events);
}
// Max listener sayisini ayarla
setMaxListeners(n) {
this._maxListeners = n;
return this;
}
}
// Test edelim
console.log('=== Kendi EventEmitter Testimiz ===\n');
const emitter = new BenimEventEmitter();
// on testi
emitter.on('mesaj', (text) => {
console.log(`Mesaj alindi: ${text}`);
});
emitter.emit('mesaj', 'Merhaba Dunya!');
emitter.emit('mesaj', 'Ikinci mesaj');
// once testi
console.log('\n--- once testi ---');
emitter.once('tek', () => console.log('Bu sadece 1 kez calisir'));
emitter.emit('tek'); // calisir
emitter.emit('tek'); // calismaz
// chaining testi
console.log('\n--- chaining testi ---');
emitter
.on('zincir', () => console.log('Zincir 1'))
.on('zincir', () => console.log('Zincir 2'))
.emit('zincir');
console.log('\nEvent isimleri:', emitter.eventNames());
console.log('mesaj dinleyici sayisi:', emitter.listenerCount('mesaj'));
Gelismis Ozellikler
Simdi temel implementasyonumuzu gelistirelim. Async event support, wildcard dinleme ve event history gibi ozellikler ekleyebiliriz. Bu tarz ozellikler production'da bazen ihtiyac duyulan seyler.
JavaScript:
// Gelismis EventEmitter
class GelismisEventEmitter extends BenimEventEmitter {
constructor() {
super();
this._gecmis = [];
this._maxGecmis = 100;
}
// Async emit - tum dinleyicilerin bitmesini bekle
async emitAsync(eventAdi, ...args) {
const dinleyiciler = this._events[eventAdi];
if (!dinleyiciler || dinleyiciler.length === 0) return false;
const kopyaDinleyiciler = [...dinleyiciler];
for (const dinleyici of kopyaDinleyiciler) {
await dinleyici.fn.apply(this, args);
if (dinleyici.once) {
this.off(eventAdi, dinleyici.fn);
}
}
return true;
}
// Event gecmisini kaydet
emit(eventAdi, ...args) {
this._gecmis.push({
event: eventAdi,
args,
zaman: Date.now()
});
// Gecmis limitini koru
if (this._gecmis.length > this._maxGecmis) {
this._gecmis.shift();
}
return super.emit(eventAdi, ...args);
}
// Gecmisi sorgula
gecmisGetir(eventAdi, limit = 10) {
let sonuclar = this._gecmis;
if (eventAdi) {
sonuclar = sonuclar.filter(g => g.event === eventAdi);
}
return sonuclar.slice(-limit);
}
// Pipe: Bir emitter'in eventlerini digerine aktar
pipe(hedefEmitter, eventler) {
for (const event of eventler) {
this.on(event, (...args) => {
hedefEmitter.emit(event, ...args);
});
}
return hedefEmitter;
}
}
// Test
async function test() {
const emitter = new GelismisEventEmitter();
// Async event
emitter.on('kaydet', async (veri) => {
await new Promise(resolve => setTimeout(resolve, 100));
console.log('Veritabanina kaydedildi:', veri);
});
emitter.on('kaydet', async (veri) => {
await new Promise(resolve => setTimeout(resolve, 50));
console.log('Cache guncellendi:', veri);
});
console.log('\n=== Async Emit ===');
await emitter.emitAsync('kaydet', { id: 1, ad: 'Test' });
console.log('Tum async islemler tamamlandi!');
// Event gecmisi
console.log('\n=== Event Gecmisi ===');
const gecmis = emitter.gecmisGetir();
gecmis.forEach(g => {
console.log(` ${g.event} - ${new Date(g.zaman).toLocaleTimeString()}`);
});
}
test();
Kendi EventEmitter'inizi Ne Zaman Kullanmalisiniz?
Pratikte Node.js'in yerlesik EventEmitter'ini kullanmalisiniz cunku daha performansli, daha cok test edilmis ve ekosistem uyumludur. Ama bazi durumlarda kendi implementasyonunuz mantikli olabilir: tarayici tarafinda calisacak bir kutuphane yaziyorsaniz (Node.js bagimliligi olmadan), ozel ozellikler gerekiyorsa (typed events, priority listeners) veya cok kucuk bir footprint istiyorsaniz.
Ben genelde Node.js'in EventEmitter'ini extend ederek kullaniyorum, sifirdan yazmak yerine. Bu yazi daha cok "nasil calistigini anlamak" icindi. Gercek projelerde tekerlek yeniden icat etmeye gerek yok. Sonraki konuda once ile tek seferlik dinlemeyi daha detayli inceleyecegiz!
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
- 2
- Görüntüleme
- 392
- Cevaplar
- 0
- Görüntüleme
- 52
- Cevaplar
- 3
- Görüntüleme
- 1K
- Cevaplar
- 8
- Görüntüleme
- 28K
