"Enter"a basıp içeriğe geçin

Node JS Event Loop Nedir & Nasıl Çalışır

Node JS kullanan birçok yazılımcı node js dilinin aslında çoklu işlemlerle çalıştığını düşünür. Aslında öyle değil. Node JS bütün işlemleri kendi APIleri aracılığıyla düzenli bir biçimde yönetmeyi amaçlar. Bunun nedeni multi processlerde kopmalar, değişkenlerin ayrıltılamaması gibi durumların önüne geçmek. Eğer gerçekten çoklu işlemler kullanmak istiyorsanız bir önceki yazımda bundan bahsettim. Ancak, tek threadde çalışan node js nasıl olurda asenkron işlemleri yapmamızı sağlar? Event loop, sonsuz bir döngüde çalışır ve arkaplanda çalışması gereken processleri dinleyerek sıraya alır ve sırayla bütün hepsini tamamlar. Gelin açıklayayım.

Stack Yapısı

Diyelim ki bir kodunuz olsun, aşağıdaki gibi.

function run(){
   console.log("çalıştım")
}

run();

Sizce bu kod nasıl çalışırdı? yapısı tam olarak şöyle gözükürdü. Önce run fonksiyonu çalışır ve ardından run fonksiyonunun içinde bulunan console.log satırı çalışırdı. Peki bunu nasıl yapıyor? bunu yapmak için stack denilen bir yığına fonksiyonunuzu koyuyor, o fonksiyonda bir sonraki fonksiyonu yığına koyuyor bu sayede projedeki her şey yığına koyulmuş oluyor. Peki aşağıdaki kod nasıl çalışırdı?

function works(){
   return works();
}

works();

hatada gözüken yere dikkat edin. Hepsi bir alttaki fonksiyonu yani works ü çağırıyor. Hata kısmında stack bölümüne bakın, works yazıyor. Bunun sebebi works fonksiyonunun stackte olması.

function works(){
   return notWorks();
}

function notWorks(){
   return works();
}

notWorks();

Bu fonksiyonun çalışmasıyla oluşacak olan kod aşağıdaki gibi olacaktır.

Sizinde gördüğünüz gibi yukarıdaki ile aynı hatayı verdi. Buradan da anlaşılacağı gibi works ve notWorks fonksiyonları birbirini zincirleme çağırdığından call stack doluyor ve en sonunda geçildiği için hata veriyor.

Senkron ve Asenkron Farkı

Senkron programlama bir işlem yapıldığında o işlemin bitmesi beklenir. Senkron kodlarda aynı anda birkaç işlem olamaz dolayısıyla bir işlem bitmeden diğer işlemi yapma gibi bir ihtimal bulunmamakta. Asenkron programlamada ise aynı anda birden fazla işlem çalışabilir ve bu işlemler beklenilmez. Asenkron işlemler web sunucularında microservices ve database bağlantıları birlikte kullanıldığı için toplu şekilde işlemleri başlatmak ve sonra bunları çıktı almak daha mantıklı olacaktır.

Buraya kadar her şey harika, fakat… nasıl olurda asenkron işlemler çalışıyor? her şey stacke aktarılıyor ise bu durumda çalışmaması gerekmez mi?

function works(){
    setTimeout(()=>{
        works();
    }, 0);
}

Yukarıdaki kod sizce ne çıktısı verir? cevap herhangi bir şey döndürmüyor olurdu. Çünkü call stack limitini engelleyen bir çözümümüz var. Callback Queue, fonksiyon sıraya sokuluyor ve böylece bütün işlemleri stack limite uğramadan yapabiliyoruz. Ancak genelde amacımız stack limiti engellemek olmuyor, stack limitten daha çok önemli olan durumların en başında asenkron programlama geliyor. Asenkron istekler bu şekilde sıraya alınıyor ve bu sıraya alınan işlem eğer call stack kısmında herhangi bir işlem çalışmıyor ise oraya çalışacak olan fonksiyonu aktarır.

Asenkron vs Senkron İşlemler

Asenkron ve senkron işlemlerde en etkileyici fark elbette senkron işlemlerin queue işlemine alınmadığından hızlı bir şekilde çalıştığı gerçeğidir. Yukarıdaki örneklere benzer bir örnek verecek olursak

function works(){
     console.log("Androsoft")
     works();
}
works();

Yaklaşık 11 bin kere ekrana “Androsoft” yazdıktan hemen sonra “Maximum call stack size exceeded” hatası verecektir. Fakat asenkron yaptığımızı varsayarsak

function works(){
    setTimeout(()=>{
        console.log("Androsoft")
        works();
    }, 0);
}
works();

Bu fonksiyon ise senkron yazılan koda kıyasla aşırı yavaş çalışacaktır. Sebebi ise arkada çalışan Callback Queue setTimeout fonksiyonunun ortaya çıkardığı bütün işlemleri kuyruğa alıyor. Eğer call stack kısmında işlemler boşalırsa sonraki işlemleri çağırıyor. Bu sayede birden fazla fonksiyonu bu şekilde çalıştırabiliriz. İsterseniz yukarıdaki kodu yine aynı şekilde sadece console.log fonksiyonunun aldığı “Androsoft” parametresini “Androsoft1” ya da herhangi bir şey kullanarak değiştirin. Yukarıdaki işlem çalışmayı durduracak ve yeni açmış olduğunuz Androsoft1 çalışacaktır. Çünkü kuyruk zaten “Androsoft” yazdırmak için bekleyen fonksiyonlarla şişmiş durumdaydı.

Event Loop

Event loop 4 ana temel özelliğin döngü içinde kullanılması ile açıklanabilir.

  1. Call Stack: Programın hangi fonksiyonunun çalıştığını takip eder ve bunları işlemek için yakalar.
  2. Callback Queue: Tamamlanmış olan işlemleri kaydetmeyi amaçlar. Event loop sürekli call stack dolu mu diye kontrol yapar ve eğer boş ise call stack kısmına callback queue işlemlerini ekler ve çalıştırır.
  3. Microtask Queue: Promise dizinlerinin depolandığı kısımdır. Eğer Callback queue boş ise microtaskler callback queueye eklenir.
  4. Event Loop: Call stack ve callback queue arasında çalışır. Sürekli olarak callback query kısmını izler ve call stacke aktarır.

Asenkron ve Optimizasyon

Event loop yapısının nasıl çalıştığını bilmeyen birisi her yere promise çalıştıracağından optimize olmaz ve performans, güç tüketimi sorunları yaşanabilir.

  1. Öncelik ayarlayın: Öncelikler ayarlayarak call stack kısmında çalışacak olan işlemleri daha ön plana alabilirsiniz.
  2. Asenkron kodunu optimize edin: Düzgün bir şekilde yazılmamış işlemler asenkron işlemlerde problem yaratabilir. Bu yüzden asenkron işlemlerin hızlıca bitmesini amaçlayın.

Aslında çok fazla optimizasyon yöntemi var. Ancak bunların hepsi kodun hızlı olmasına yönelik. Bu yüzden en önemli mesele yazacağınız kodun optimize olup olmadığıdır.

Async – Await

Async, çağırdığınızda stack değilde event loopda queueye işleminizi alan keyworddür. await kullanarak bitmesini bekleyebiliriz. Ancak await sadece queueye alınmış işlemlerde yani asenkron işlemlerde çalışır.

Sonsöz

Node JS dilinin bu şekilde çalıştığını bilmek beni şaşırtmıştı. Sizlerle de paylaşmak istedim. Buraya kadar okuduysanız teşekkür ederim 🙂

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir