Skip to main content
•7 dk okuma

Node.js’te EventEmitter ile Olay Odaklı Programlama Rehberi

Mustafa Kürşad BaşerMustafa Kürşad BAŞER
••
Node.js’te EventEmitter ile Olay Odaklı Programlama Rehberi

Node.js’in olay odaklı yapısı sık tekrarlanan bir cümle, ama pratikte ne demek olduğu genelde havada kalır. Somut karşılığı events modülündeki EventEmitter sınıfı, sadece 200 satırdan biraz fazla bir kod parçası. Node’un kendi içindeki pek çok yapı ondan türüyor: fs.ReadStream, http.Server, net.Socket ve process nesnesi dahil. Yani server.on('request', ...) yazdığınız her seferde EventEmitter kullanıyorsunuz.

EventEmitter Sınıfı Nedir?

EventEmitter, Node.js’in events modülünden gelen bir sınıf. Yaptığı iş basit. Bir yerde "şu oldu" dersiniz, o olayı dinleyen fonksiyonlar sırayla çalışır. Kazanç, olayı tetikleyen kodun kendisini dinleyenleri hiç tanımaması. Dosya yükleme bittiğinde bir yerde günlük kaydı atılsın, bir yerde veri tabanına yazılsın, bir yerde de bildirim gönderilsin istiyorsanız; yükleme kodunun bu üçünden haberi olmasına gerek kalmaz.

EventEmitter, Node.js’in temel taşlarından biri. Olayları tetiklemek için emit() metodunu, dinlemek içinse on() metodunu kullanıyoruz. Bu kadar basit bir mekanizma olmasına rağmen, arkasında güçlü bir mantık yatıyor ki bunu birazdan daha iyi anlayacağız.

Node.js EventEmitter yapısında bir olayın tek noktadan tetiklenip birden fazla dinleyici tarafından karşılanmasını gösteren açıklamalı diyagram.

EventEmitter ve Observer Pattern İlişkisi

EventEmitter’ı anlamak için tasarım desenlerine bir göz atmakta fayda var. Bu sınıf, Observer Pattern (Gözlemci Deseni) üzerine kurulu. Observer Pattern, bir nesnenin (subject) durumundaki değişiklikleri izleyen diğer nesneleri (observers) otomatik olarak bilgilendiren bir mekanizma sunar. Mesela, bir haber sitesinde yeni bir makale yayınlandığında abone olan kullanıcıların haberdar edilmesi gibi düşünebilirsiniz. EventEmitter da tam olarak bunu yapıyor: Bir olay (durum değişikliği) tetiklendiğinde, bu olayı dinleyen gözlemciler (listeners) harekete geçiyor.

Bu aslında Observer Pattern’in ta kendisi. Yayıncı olayı duyurur, aboneler tepki verir, ikisi birbirini tanımaz. Node.js bu deseni sonradan eklenen bir kütüphaneye bırakmamış, doğrudan çekirdeğe koymuş. fs akışları, HTTP sunucusu ve process nesnesi dahil olmak üzere Node içindeki pek çok yapı EventEmitter’dan türüyor. Yani farkında olmadan zaten kullanıyorsunuz.

EventEmitter Nasıl Kullanılır?

EventEmitter’ı kullanmak oldukça kolay. İlk adım olarak, Node.js’in yerleşik events modülünü projenize dahil etmeniz gerekiyor. Ardından bir EventEmitter nesnesi oluşturup olayları tanımlayabilir ve tetikleyebilirsiniz. Hadi bunu bir örnekle görelim.

javascript
const EventEmitter = require('events'); const myEmitter = new EventEmitter(); myEmitter.on('selam', () => {  console.log('Merhaba, olay gerçekleşti!');}); myEmitter.emit('selam');

Bu kodda ne yaptık? Önce events modülünden EventEmitter sınıfını aldık ve myEmitter adında bir nesne oluşturduk. Sonra on() metoduyla bir olay dinleyicisi ekledik, burada olayımız selam adını taşıyor ve tetiklendiğinde bir mesaj yazdırıyor. Son olarak emit() metoduyla bu olayı ateşledik. Çıktı şu şekilde olacak: “Merhaba, olay gerçekleşti!”. Gördüğünüz gibi, oldukça sade ve etkili bir yapı.

Bu ipuçları, EventEmitter’ı daha verimli kullanmanıza yardımcı olacak ve olası tuzaklardan kaçınmanızı sağlayacak. Özellikle büyük projelerde bu detaylar hayat kurtarabilir!

Olaylara Veri Gönderme

EventEmitter’da emit ile gönderilen verinin dinleyici fonksiyonlara nasıl iletildiğini gösteren açıklamalı akış diyagramı.

EventEmitter’ın en güzel yanlarından biri, olaylarla birlikte veri de gönderebilmeniz. Bu, dinleyicilere ek bilgi sağlayarak daha dinamik bir yapı kurmanızı mümkün kılıyor. Hadi bunu bir örnekle inceleyelim.

javascript
const EventEmitter = require('events'); const myEmitter = new EventEmitter(); myEmitter.on('kullaniciGiris', (kullaniciAdi) => {  console.log(`${kullaniciAdi} giriş yaptı!`);}); myEmitter.emit('kullaniciGiris', 'Ahmet');

Bu kodda, kullaniciGiris olayını tetiklerken bir kullanıcı adı (Ahmet) gönderiyoruz. Dinleyici bu veriyi alıyor ve konsola “Ahmet giriş yaptı!” yazıyor. İsterseniz birden fazla veri de gönderebilirsiniz; örneğin, kullanıcı adı ve giriş zamanı gibi. Bu esneklik, EventEmitter’ı gerçek projelerde çok kullanışlı hale getiriyor.

Peki ya bir olaya birden fazla dinleyici eklemek mümkün mü? Tabii ki mümkün. Bu, farklı aksiyonların aynı olayla tetiklenmesini sağlıyor. Hadi bir dosya yükleme senaryosu üzerinden bunu görelim.

javascript
const EventEmitter = require('events'); const myEmitter = new EventEmitter(); myEmitter.on('dosyaYuklendi', (dosyaAdi) => {  console.log(`${dosyaAdi} başarıyla yüklendi.`);}); myEmitter.on('dosyaYuklendi', (dosyaAdi) => {  console.log('Yükleme tamamlandı, veritabanına kaydediliyor...');}); myEmitter.emit('dosyaYuklendi', 'belge.pdf');

Çıktı şu şekilde olacak: - belge.pdf başarıyla yüklendi. - Yükleme tamamlandı, veritabanına kaydediliyor... Gördüğünüz gibi, aynı olay için iki farklı dinleyici tanımladık ve her biri kendi işini yaptı. Bu yapı, modüler ve esnek bir kod yazmanızı sağlıyor.

EventEmitter vs. RxJS ve EventTarget: Hangisi Daha Uygun?

EventEmitter, Node.js için harika bir araç, ama olay odaklı programlama için başka alternatifler de var. Peki, EventEmitter’ı RxJS veya tarayıcıdaki EventTarget’tan ayıran nedir? Kısa bir karşılaştırma yapalım.

EventEmitter (Node.js): Basit, lightweight ve Node.js’e özgü. Olayları tetiklemek ve dinlemek için minimal bir API sunuyor. Küçük ve orta ölçekli projelerde kullanımı çok kolay. Ancak, karmaşık veri akışlarını yönetmek için sınırlı.

RxJS: Reaktif programlama için tasarlanmış güçlü bir kütüphane. Observable’lar ile zamanla değişen veri akışlarını (stream) yönetmek için ideal. EventEmitter’a göre daha karmaşık ama büyük projelerde veri akışını kontrol etmek için çok daha esnek.

EventTarget (Tarayıcı): Tarayıcıda DOM olaylarını (click, submit gibi) yönetmek için kullanılan standart bir API. EventEmitter’a benzer, ama Node.js’te değil, yalnızca tarayıcı ortamında çalışır.

Hangi aracı seçeceğiniz projenizin ihtiyaçlarına bağlı. Eğer sadece Node.js içinde basit bir olay yönetimi istiyorsanız, EventEmitter yeter de artar bile. Ancak karmaşık veri akışları veya reaktif bir yapı gerekiyorsa, RxJS’e göz atabilirsiniz. Tarayıcı tarafında çalışıyorsanız da EventTarget zaten elinizin altında.

EventEmitter Nerelerde Kullanılır?

Belki şu ana kadar her şey harika gözüküyor, peki ya gözünüzde bir şey canlanmadı mı? Gerçek dünyada nerelerde kullanılıyor? İşte farklı sektörlerden birkaç örnek:

E-ticaret: Bir online mağazada, bir ürün stokta kalmadığında stokBitti olayı tetiklenebilir. Dinleyiciler, bu durumda kullanıcıya bildirim gönderir, stok raporunu günceller ve tedarikçiye uyarı yollar.

Oyun Geliştirme: Çok oyunculu bir oyunda, bir oyuncu hamle yaptığında oyuncuHareketi olayı tetiklenir. Diğer oyuncuların ekranları bu olaya dinleyici ekleyerek anında güncellenir.

IoT (Nesnelerin İnterneti): Bir sensör sıcaklık değişimi algıladığında sicaklikDegisti olayı yayılır. Dinleyiciler, bu veriyi analiz eder, bir uyarı gönderir veya cihazın davranışını değiştirir.

Bu örnekler, EventEmitter’ın ne kadar çok yönlü olduğunu gösteriyor. Sektör fark etmeksizin, olay odaklı bir yapıya ihtiyaç duyduğunuz her yerde EventEmitter’ı düşünebilirsiniz.

Kaçınmanız Gereken 3 Yaygın Hata

EventEmitter’ı kullanırken bazı tuzaklara düşmek mümkün. İşte en sık karşılaştığım hatalar ve bunlardan nasıl kaçınacağınız:

  1. 1Dinleyici Sızıntısı: Kullanılmayan dinleyicileri kaldırmazsanız bellek sızıntılarıyla karşılaşabilirsiniz. Çözüm: removeListener() veya removeAllListeners() kullanın.
  2. 2Hataları Göz Ardı Etmek: error olayını dinlemezseniz, atılan hatalar uygulamanızı çökertebilir. Çözüm: Her zaman bir hata dinleyicisi ekleyin.
  3. 3Karmaşık Olay İsimleri: Tutarsız veya karışık olay isimleri (örneğin, evt1, eventX) kodun okunmasını zorlaştırır. Çözüm: Anlamlı ve standart isimler seçin.

Bu hatalara dikkat ederek EventEmitter’ı daha sorunsuz bir şekilde kullanabilirsiniz. Ben de ilk başlarda dinleyici sızıntısıyla uğraşmıştım, ama işin mantığını çözünce bunu yönetmek çok daha kolay.

Sonuç

Toparlarken pratikte başınızı ağrıtacak üç şeyi bırakayım. Birincisi, emit() çağrısı senkrondur; dinleyiciler sırayla ve tetikleyen kodun içinde çalışır. Biri ağır bir iş yaparsa olay döngüsünü bloklar. İkincisi, error olayının özel bir statüsü var: dinleyicisi yoksa Node süreci çökertir, o yüzden her emitter için bir error dinleyicisi tanımlayın. Üçüncüsü, on() ile eklediğiniz dinleyicileri işiniz bittiğinde off() ile kaldırın; uzun ömürlü bir emitter’a sürekli dinleyici eklemek klasik bir bellek sızıntısı kaynağıdır. Zaten aynı olaya onbirinci dinleyiciyi eklediğinizde Node size uyarı verecektir.

Son olarak birkaç pratik alışkanlık. Olay isimlerini user:created gibi ad alanıyla yazın; proje büyüdüğünde update diye bir olayın kime ait olduğunu kimse bilemez. Dinleyici sayısı için varsayılan uyarı sınırı 10; gerçekten daha fazlasına ihtiyacınız varsa emitter.setMaxListeners(20) ile bunu bilinçli olarak yükseltin, uyarıyı görmezden gelmeyin. Bir de once() metodunu hatırlayın: tek seferlik olaylarda dinleyiciyi kendisi kaldırdığı için sızıntı riskini sıfırlar. Bu yazımı beğendiyseniz, diğer yazılarıma da göz atmanızı öneririm. Tekrar görüşmek üzere!

Bu yazıyı paylaş

Link kopyalandı!
Mustafa Kürşad Başer
Yazar

Mustafa Kürşad Başer

Kıdemli Yazılım Mühendisi

Karmaşık sorunlara zarif çözümler üretmekten keyif alan, tutkulu bir yazılım mühendisi. Kodlamanın ötesinde, teknoloji, sanat ve insan bilincinin kesişim noktalarını keşfetmekle derinden ilgileniyor.