Node.js ‘in Event modülünü öğrenmeden önce javascriptin event driven programming mantığını yani olay odaklı programlama mantığını anlamamız gerekiyor. Peki nedir bu olay odaklı programlama ?
Event Driven Programming
İsminden de anlaşıldığı gibi sistem içerisinde bir olay gerçekleştiğinde diğer elemanların o olaya göre tepki göstermesidir. Örnek verecek olursan DOM üzerinde bir butona tıklanması olayı gerçekleştiğinde bir işlem yapabiliriz. Böyle bir durumda sistem o butonu tanımlamamızı sağlayan bir bilgi ile birlikte sistemde olayın gerçekleştiğini yayar(emir). Birisinin ayağa kalkıp “ben x kişisiyim ve y kişisi bana tıkladı” diye bağırması gibi düşünebiliriz. Daha sonra bu bilgiye göre bir işlem yapılacaksa bu sinyali bekleyen bölüm işareti almış olur ve tetiklenir- harekete geçer. İşlemler bu şekilde çalışır. Şimdi node.js ‘in bize sunduğu “events” modülüne bir bakalım.
Events Modülü İle Olaylar Olaylar
Şimdi olay odaklı programlamanın nasıl çalıştığını biliyoruz. Peki bu javascriptte nerede kullanılıyor? Hemen hemen her yerde! Node.js ile bir siteye istek atarken, veya javascriptte DOM’u dinlerken, eşzamanlı bir işlem yürüteceğimiz zaman veya kendi belirlediğimiz koşullar oluştuğunda bir olay döngüsünü tetikleyebiliriz. İşte bu noktada Node.js bize “events” adında bir modül sağlıyor. Bu modül yardımıyla olay sinyalleri üretip, dinleyip manipüle edebiliyoruz.
const EventEmitter = require("events");
Yukarıdaki satıyla projemize events modülünü dahil etmiş olduk. Şimdi yeni bir emitter objesi oluşturalım. Oluşturduğumuz bu objeyle hem bir olay dinleyebiliriz hem de bir olay yayabiliriz :
const emitter = new EventEmitter();
Öncelikle bir olay döngüsü dinlemeyi(listen) yazalım. Bu şekilde bizim verdiğimiz isimde bir olay gerçekleştiği zaman ikinci parametre olarak verdiğimiz callback fonksiyonu çalışacak.
emitter.on("messageLogged", function(arg) { //Burada kullanılan arg e olabilir veya herhangi bir isim olabilir.
console.log("listener called", arg);
});
Bu kod ile artık “messageLogged” adında bir olay gerçekleştiğinde konsola bu olayın gerçekleştiğine dair bir mesaj yazacağız. .on fonksiyonunu aslında onClick’ten tanıyoruz. Türkçeye çevirdiğimizde “click” olayı gerçekleşince gibi bir anlam çıkıyor. Birinci argümanda olayın adını verirken ikinci argüman da o olay gerçekleşince çalışacak fonksiyonu belirlemiş oluyoruz. Ayrıca bu fonksiyonun aldığı argüman da sinyal ile birlikte yayılan veriyi işaret eder.
Şimdi birisi bu olayı dinliyorsa birisi de bu olayı yaymalı. Elimizde hazır bir emitter objesi varken bu obje yardımıyla aynı isimde bir olay yayalım ve sonuca bakalım :
emitter.emit('messageLogged', {id : 1, url : 'url'});
.emit fonksiyonu da process içinde bir sinyali istediğimiz veriyle birlikte yaymamızı sağlıyor. Tahmin edeceğiniz üzere birinci argüman sinyalin adını belirtirken ikinci argüman yayılacak veriyi belirtiyor. Kodu yorumlarla birlikte genel olarak yazacak olursak :
//Buradaki tüm fonksiyonlar arrow function şeklinde yazılabilir.
const EventEmitter = require("events"); //Class names beginning UpperCase
const emitter = new EventEmitter();
//Emitting means signaling an event at application making noise
//Raise an listener
//Burada eklediğimiz event listener, uygulamada bu sinyal oluştuğunda bunu yakalar. Bu listener bağlamak için .on metodunu kullanıyoruz. Bu metodun birinci parametresi dinlemek istediğimiz event'ın adı ikinci parametresi ise sinyal yakalandığında çalıştırılacak signal handler fonksiyonu.
emitter.on("messageLogged", function(arg) { //Burada kullanılan arg e olabilir veya herhangi bir isim olabilir.
console.log("listener called", arg);
});
//Raise an event
//Örnek olarak burada eventımıza bir isim veriyoruz. İlerleyen zamanlarda her log işlemi yapıldığında bu sinyalin uygulamada yankılanmasını yani emit edilmesini sağlayacağız.
emitter.emit('messageLogged', {id : 1, url : 'url'}); // Best practice : Birden fazla argüman gönderilecekse JSON olarak gönder.
//emitter.emit('messageLogged', 1, "url"); =>Verdiğimiz sonraki parametreler bu event ile taşınacak parametrelerdir. Yani event argumentstir.
//Real world örneklerinde doğrudan EventEmitter ile işlemler yapılmaz genel olarak bir sınıf yazılır ve bu sınıf içinde gerekli bütün eventler gönderilir veya yakalanır.
Buraya kadar her şey güzeldi. Ancak gerçek uygulamalar üzerinde çalışırken bu kadar basit yapılar kurulmuyor. İşin doğrusu EventEmitter doğrudan bir obje olarak bile kullanılmıyor. Javascriptte hemen her şeyin bir obje olduğunu biliyoruz. Bu yüzden “events” sınıfını miras alan(extend) bir sınıf yazıp bu şekilde kullanıyoruz.
Örnek olarak bir logger sınıfı yazmak istiyoruz diyelim. Bu sınıf yardımıyla istediğimiz zaman bir olayın tetiklenmesini ve tetiklendikten sonra uzaktaki bir url’ye log bilgisi göndermesini istiyoruz. Öncelikle logger.js adında bir dosya oluşturarak events sınıfını extend eden kendi sınıfımızı yazalım.
logger.js
const EventEmitter = require("events");
//const emitter = new EventEmitter();
var url = "http://mylogger.com/log";
//Classı EventEmitter dan türeterek bu sınıfın sahip olduğu bütün özellikelere sahip oluyoruz.
class Logger extends EventEmitter{
log(message){
console.log(message);
this.emit('messageLogged', {id : 1, url : url});//burada emitter yerine this diyoruz çünkü artık biz de emitter objesiyiz.
}
}
module.exports = Logger;
Burada bir events’i require ile eklediğimiz sınıf EventEmitter oldu. Daha sonra Logger sınıfını yazarken de bu sınıftan extend ettik. Daha sonra da bu Logger sınıfımıza bir mesaj argümanı alan bir log fonksiyonu tanımlıyoruz. Emit ederken artık EventEmitter demiyoruz çünkü this referansının işaret ettiği Logger sınıfı, EventEmitter sınıfının bütün özelliklerine zaten sahip. Bu yüzden this.emit ile istediğimiz bilgiyi paylaşabiliyoruz.
Şimdi bunu kullandığımız bir event.js dosyası oluşturalım :
event.js
const EventEmitter = require("events"); //Class names beginning UpperCase
const Logger = require('./logger');
const logger = new Logger();
//Export ettiğimiz loggerın EventEmitter objesi olduğunu extend ettiği için biliyoruz. Dolayısıyla bu obje üzerinde .on çağırabiliyoruz.
logger.on("messageLogged", function(arg) { //Burada kullanılan arg e olabilir veya herhangi bir isim olabilir.
console.log("listener called", arg);
});
logger.log('message');
Evet artık kendi yazdığımız ve process boyunca kendi objesinin yaydığı sinyalleri dinleyebilecek bir sınıfımız hazır. Ayrıca bu sınıfı oluşturup bir de buna bir listener yani olay dinleyecek fonksiyonu da .on ile yazdık. daha sonra da log fonksiyonuyla bir olay yaydığımızda bu yakalanacak ve listener called yazısı ekrana basılacak.