Node.js - Events

2022-01-12

Node.js’in yapısına kısaca değindiğimiz Asenkron Fonksiyonlar makalesinde de söylediğimiz gibi; Node.js Single Threaded çalışan ve bir Event Loop üzerinden işlemlerini yürüten bir Javascript run-time environment’tir. Events, bu yapıyı daha iyi kavramamızı sağlayacak bir modüldür.

Javascript, Olay Yönelimli (Event-Driven) bir programlama dili olduğundan olaylar(events) sıkça kullanılan yapılardır. Events kavramını bir cümleyle özetlemek istersek, bir olay oluştuğunda tetiklenen ve bir dinleyici(listener) tarafından yakalanarak ilgili fonksiyonların çalışmasını sağlayan yapıya verilen isimdir. “events” modülü built-in bir modüldür bu nedenle herhangi bir kurulum gerektirmez.

Nasıl Kullanılır?

EventEmitter’i kullanmak için öncelikle import etmemiz gerekir. Bunun için events.js adında bir dosya oluşturup, destructuring assignment kullanarak events modülü içindeki EventEmitter sınıfını import edelim. Ardından bu sınıfa ait bir nesne üretelim.

// events.js
const { EventEmitter } = require("events");
const eventEmitter = new EventEmitter();

Şimdiyse olay dinleyici(event listener) tanımı yapalım. Event Listener, bir olay gerçekleştiğinde bu olayı yakalayıp gerekli işlemleri yapmamızı sağlayan yapılardır. Tanımlamanın iki yöntemi var. Birincisi on() metodu kullanılarak yapılan tanım.

// events.js
const { EventEmitter } = require("events");
const eventEmitter = new EventEmitter();

eventEmitter.on("event", (data) => {
    console.log("Message is handled", data);
});

İkinci yöntem ise addListener metodudur.

const { EventEmitter } = require("events");
const eventEmitter = new EventEmitter();

eventEmitter.on("event", (data) => {
    console.log("Event is handled", data);
});

eventEmitter.addListener("message", (data) => {
    console.log("Message is handled", data);
});

İki metodun da aldığı temel iki parametre var. İlk parametre dinlenecek olayın adı (eventName), ikincisi ise olay gerçekleştiğinde çalışacak fonksiyondur.

_config.yml

Bir olay olduğundaysa emit() metodu ile tetiklemeyi gerçekleştirebiliriz. Uygulamamıza emit metotlarını da ekleyelim.

const { EventEmitter } = require("events");
const eventEmitter = new EventEmitter();

eventEmitter.on("event", (data) => {
    console.log("Event is handled", data);
});

eventEmitter.addListener("message", (data) => {
    console.log("Message is handled", data);
});

eventEmitter.emit("message", "ender", "can", 30);
eventEmitter.emit("event", { firstName: "ender", lastName: "can", age: 30 });

Bu uygulamayı çalıştırdığımızda çıktı aşağıdaki gibi olacaktır.

_config.yml

Not: Bir eventEmitter nesnesi üzerinden tanımlanmış bir listener, sadece aynı nesneden tetiklenen emit leri yakalar. Bu nedenle referansı kaybetmemek önemlidir.

event isimli listener, gönderdiğimiz objenin tamamını ekrana basabilmişken, message isimli listener ise sadece “ender” çıktısını üretti. Bunun sebebi, emit ile gönderilen parametre sayısı ile listener fonksiyonundaki parametre sayısının farklı olması. Kısaca emit() metodu ile kaç parametre gönderiliyorsa listener fonksiyonunda da o kadar parametre alınmalıdır. message event listener tanımını aşağıdaki şekilde güncellediğimizde tüm parametreleri alabiliriz.

eventEmitter.addListener("message", (firstName, lastname, age) => {
    console.log("Message is handled", firstName, lastname, age);
});

Ayrıca destructuring ile gönderilen tüm parametreleri tek değişkende de toplayabiliriz. Bu değişken bir dizi olacaktır. Nasıl tanımlayacağımızı görelim:

eventEmitter.addListener("message", (...args) => {
    console.log("Message is handled", args);
});

Kodun son hali ve çıktısı aşağıdaki gibi olacaktır.

const { EventEmitter } = require("events");
const eventEmitter = new EventEmitter();

eventEmitter.on("event", (data) => {
    console.log("Event is handled", data);
});

eventEmitter.addListener("message", (...args) => {
    console.log("Message is handled", args);
});

eventEmitter.emit("message", "ender", "can", 30);
eventEmitter.emit("event", { firstName: "ender", lastName: "can", age: 30 });

_config.yml

Not: Aynı listener’dan birden fazla oluşturabiliriz. Bu durumda ilgili event tetiklendiğinde(emit) iki listener’da çalışacaktır.

Temel hatlarıyla bir olayın(event) nasıl tetiklendiğini ve nasıl dinlendiğini gördük. Biraz daha detaylara inelim.

Listener İşlemleri

Şimdiye kadar gördüğümüz on() ve addListener() metodları, tanımlandığı isimle tetiklenen tüm olayları dinler. Peki ya bir olayı sadece bir kez dinlemek ve bir kez çalıştırmak istersek? Bu durumda EventEmitter sınıfının once() metoduna ihtiyaç duyarız. Tanımlama biçimi on() ve addEventListener() metotlarıyla birebir aynıdır.

eventEmitter.once("eventOnce", (...args) => {
    console.log("eventOnce is handled", args);
});

Test edebilmek için her bir event için ikişer kez emit() yapalım. Son durumda uygulamamız ve çıktımız:

const { EventEmitter } = require("events");
const eventEmitter = new EventEmitter();

eventEmitter.on("event", (data) => {
    console.log("Event is handled", data);
});

eventEmitter.addListener("message", (...args) => {
    console.log("Message is handled", args);
});

eventEmitter.once("eventOnce", (...args) => {
    console.log("eventOnce is handled", args);
});

eventEmitter.emit("message", "ender", "can", 30);
eventEmitter.emit("message", "data");
eventEmitter.emit("event", { firstName: "ender", lastName: "can", age: 30 });
eventEmitter.emit("event", { firstName: "harika", lastName: "can", age: 30 });
eventEmitter.emit("eventOnce", { firstName: "ender", lastName: "can", age: 30 });
eventEmitter.emit("eventOnce", { firstName: "ender", lastName: "can", age: 30 });

_config.yml

Uygulamanın 20. ve 21. satırlarında eventOnce isimli olay iki kez tetiklenmesine rağmen listener sadece birini ekrana bastı. Çünkü once() metodu, ilk tetiklemeden itibaren dinlemeyi durdurur.

EventEmitter sınıfına ait farklı metotlar da mevcuttur. Ben burada en sık kullandıklarımıza değindim. Tüm metotlara ve açıklamalarına Node.js dokümantasyonundan erişebilirsiniz. Node.js’in önemli özelliklerinden birini daha uygulamış olduk. İyi çalışmalar dilerim :)