Node.js, yüksek performans ve ölçeklenebilirlik sunan, olay güdümlü bir asenkron çalışma modeline sahip bir JavaScript çalışma zamanıdır. Asenkron çalışma modeli, Node.js'in yüksek trafik altında bile hızlı ve verimli çalışmasını sağlar. Bu makalede, Node.js'in asenkron yapısını ve bu yapıdaki önemli bir kavram olan Timer Queue'yu detaylı bir şekilde inceleyeceğiz.
Node.js, tek bir olay döngüsü (event loop) üzerinden çalışır. Bu, birden fazla işlemin aynı anda yürütülmesini sağlar ve yüksek performans sunar. Olay döngüsü, sıraya alınmış işlemleri (callback fonksiyonları) belirli bir sıra ve öncelik dahilinde yürütür. Bu sıranın anlaşılması, Node.js'de verimli asenkron kod yazmanın temelidir.
Asenkron yapının önemli bileşenleri şunlardır:
Call Stack (Çağrı Yığını)
Event Loop (Olay Döngüsü)
Callback Queue (Geri Çağırma Kuyruğu)
Microtask Queue (Mikro Görev Kuyruğu)
Timer Queue (Zamanlayıcı Kuyruğu)
I/O Queue (Giriş/Çıkış Kuyruğu)
Timer Queue, belirli bir süre sonra yürütülmesi gereken callback fonksiyonlarını barındıran bir veri yapısıdır. Timer Queue'ya callback fonksiyonları eklemek için setTimeout
veya setInterval
fonksiyonları kullanılır. Bu kuyruk, diğer microtask kuyruklarına göre daha düşük bir önceliğe sahiptir.
setTimeout ve setInterval fonksiyonlarının sözdizimi şu şekildedir:
setTimeout(callbackFunction, delay);
setInterval(callbackFunction, delay);
callbackFunction
: Gecikme süresi sona erdiğinde yürütülecek fonksiyon.
delay
: Milisaniye cinsinden gecikme süresi.
Bu bölümde, Timer Queue'nun nasıl çalıştığını ve öncelik sırasını anlamak için birkaç örnek yapacağız.
İlk örneğimizde, setTimeout
, process.nextTick
ve Promise.resolve
kullanarak farklı kuyrukların öncelik sırasını test edeceğiz.
setTimeout(() => console.log("setTimeout 1"), 0);
setTimeout(() => console.log("setTimeout 2"), 0);
setTimeout(() => console.log("setTimeout 3"), 0);
process.nextTick(() => console.log("process.nextTick 1"));
process.nextTick(() => {
console.log("process.nextTick 2");
process.nextTick(() =>
console.log("next tick içinde iç next tick")
);
});
process.nextTick(() => console.log("process.nextTick 3"));
Promise.resolve().then(() => console.log("Promise.resolve 1"));
Promise.resolve().then(() => {
console.log("Promise.resolve 2");
process.nextTick(() =>
console.log("Promise then bloğu içinde iç next tick")
);
});
Promise.resolve().then(() => console.log("Promise.resolve 3"));
Bu kodu çalıştırdığımızda, beklediğimiz çıktı:
process.nextTick 1
process.nextTick 2
next tick içinde iç next tick
process.nextTick 3
Promise.resolve 1
Promise.resolve 2
Promise.resolve 3
Promise then bloğu içinde iç next tick
setTimeout 1
setTimeout 2
setTimeout 3
Bu örnek, process.nextTick
ve Promise çözümlemelerinin, timer queue'daki setTimeout
fonksiyonlarından önce yürütüldüğünü gösterir.
İkinci örnekte, setTimeout
fonksiyonu içinde process.nextTick
kullanacağız.
setTimeout(() => console.log("setTimeout 1"), 0);
setTimeout(() => {
console.log("setTimeout 2");
process.nextTick(() =>
console.log("setTimeout içinde iç next tick")
);
}, 0);
setTimeout(() => console.log("setTimeout 3"), 0);
process.nextTick(() => console.log("process.nextTick 1"));
process.nextTick(() => {
console.log("process.nextTick 2");
process.nextTick(() =>
console.log("next tick içinde iç next tick")
);
});
process.nextTick(() => console.log("process.nextTick 3"));
Promise.resolve().then(() => console.log("Promise.resolve 1"));
Promise.resolve().then(() => {
console.log("Promise.resolve 2");
process.nextTick(() =>
console.log("Promise then bloğu içinde iç next tick")
);
});
Promise.resolve().then(() => console.log("Promise.resolve 3"));
Bu kodu çalıştırdığımızda, beklediğimiz çıktı:
process.nextTick 1
process.nextTick 2
next tick içinde iç next tick
process.nextTick 3
Promise.resolve 1
Promise.resolve 2
Promise.resolve 3
Promise then bloğu içinde iç next tick
setTimeout 1
setTimeout 2
setTimeout içinde iç next tick
setTimeout 3
Bu örnek, setTimeout
içinde process.nextTick
kullanıldığında, nextTick
fonksiyonunun timer queue'daki diğer callback fonksiyonlarından önce yürütüldüğünü gösterir.
Son örnekte, farklı gecikme sürelerine sahip setTimeout
fonksiyonlarını kullanacağız.
setTimeout(() => console.log("setTimeout 1"), 1000);
setTimeout(() => console.log("setTimeout 2"), 500);
setTimeout(() => console.log("setTimeout 3"), 0);
Bu kodu çalıştırdığımızda, beklediğimiz çıktı:
setTimeout 3
setTimeout 2
setTimeout 1
Bu örnek, en kısa gecikme süresine sahip setTimeout
fonksiyonunun önce yürütüldüğünü gösterir.
Timer Queue, aslında bir Min Heap veri yapısıdır. Ancak, anlamayı kolaylaştırmak için FIFO (First In, First Out) sırasıyla çalıştığını düşünebiliriz. Bu, en kısa gecikme süresine sahip callback fonksiyonlarının önce sıraya alınmasını ve yürütülmesini sağlar.
Bu makalede, Node.js'in asenkron yapısını ve Timer Queue'nun nasıl çalıştığını detaylı bir şekilde inceledik. Timer Queue, Node.js'in olay güdümlü asenkron çalışma modelinde önemli bir rol oynar ve doğru anlaşıldığında asenkron kod yazmayı daha etkili hale getirir. Timer Queue'nun mikro görev kuyruklarından daha düşük önceliğe sahip olduğunu ve FIFO sırasına göre çalıştığını öğrendik. Bu bilgiler, Node.js uygulamalarında daha verimli ve öngörülebilir asenkron işlemler gerçekleştirmek için kritik öneme sahiptir.