Javascript Nasıl Çalışır? Javascript Runtime ve V8 Motoru
Bu yazımda, Javascript engine ve onun anatomisini ele alacağım. Ayrıca Javascriptin call stackine event loop’a task queuelarına ve Javascripti javascript yapan bir çok parçasınada değineceğim.
Javascript motorunun anatomisine göz atmadan önce, biraz javasripten ve onun hikayesinden söz edelim.
Kısaca Javascript
Javascript yorumlanan bir dildir. Bunun anlamı javascript kaynak kodu browsera göndermeden önce compile olmak zorunda değil. Bir interpreter javascript satırını alır ve senin için çalıştırır.
Javascript ayrıca C /C++ aksine dinamik tipli bir dil. Ne demek istediğime gelecek olursak var keywordü kullanılarak int, string boolean ve hatta daha karmaşık object ve array türünde bir değişken tanımlayabilirsin. Yani type belirtmene gerek yok.
Type belirtmemenin vermiş olduğu esneklik javascriptin hızını etkiler. Statik tipli bir dil çok verimli bir makine kodu üretir. Çünkü datanın type’ı ve size’ı hakkında bilgiyi vermiş oluruz.
C/C++ gibi statik yazılmış dillerde geliştirme yapmak zor olsada performanslı çalışır.
Javascriptin Hikayesi
Hız konusunda Javascript bu kadar zayıfsa neden bu şekilde tasarlandı diye sorabilirsiniz. Bunun için hikayesine bakmak lazım.Sadece ismini biliyoruz hikayesini değil🙂
Webin ilk günlerinde, web browserlar statik sayfaları görüntülemek için kullanıldı.Bu sayfalar etkilişimli sayfalar değildi. Biraz etkileşim eklemek için Brendan Eich tarafından 1995 yılında Netscape tarayıcısında yeni bir dil tanımlandı.Bu yeni dil Javascripti( ilk zamanlar LiveScript) ve Brendanın bu dili tasarlaması 10 günü aldı.
10 günde iyi bir iş çıkmaz ama 10 günlük çabayla, javascript inanılmazdı. Diğer diller ve eklentiler ActionScript, Silverlight ve Flash karşısında çok daha güçlüydü.
Javascript performans göz önünde bulundurarak tasarlanmamıştır. Yalnızca bir tarayıcı içinde çalışması ve DOM ile çalışması için API sağlaması gerekiyordu. Ancak birçok tarayıcı bunu kendi yöntemleriyle benimsemeye çalıştığından, standartlaşması gerekiyordu.
Ecma International Javascripti standartlaştırma organizasyonudur ve Technical Comittee 39 (TC39) bu standartı yönetir. Bu standart EcmaScript olarak bilinir ve EcmaScript ifadesi Javascript yerinede kullanılır.
Javascript Motorunun Anatomisi
EcmaScript standartı, bir javascript programının tüm tarayıcılarda tam olarak aynı şekilde çalışması için javascript’in tarayıcı tarafından nasıl uygulanması gerektiğini söyler, ancak Javascriptin bu tarayıcılarda nasıl çalışması gerektiğini söylemez.
Her browser Javascript kodunu çalıştıracak bir Javascript motoru barındırır.Netscape browserınınki SpiderMonkeydir. Bu motor optimizasyonu olmayan ilkel bir interpreterdir. Javascript kodunu bu motorla çalıştırmak yavaştı fakat yinede çalıştırıyordu.
Yukarıdaki diyagramdan da görebileceğiniz gibi, Javascript motorunun ilk işi Javascript source kodunu almak ve CPU’nun anlayacağı binary talimatlara yani makine koduna derlemek.
İlkel bir javascript motoru bir baseline compiler içerir. Baseline compiler Javascript kodunuzu bytecode’a yani bir ara koda(intermediate representation) dönüştürür. Ardından bir interpreter bu bytecodeları makine koduna yani binarycode’a dönüştürür.
Baseline compilerın işi kodu en hızlı sürede compile etmek ve daha az optimize bytecodelar oluşturmak. Interpreter optimize edilmemiş kodlarla çalıştığı için, uygulama hızı yavaş ama applicationın önyükleme süresi çok daha kısa olacaktır.
SpiderMonkey son derece optimize edilmiş makine kodu üretmek için karmaşık bir motora dönüştü ve şu anda Firefox tarafından kullanılıyor. Source code’u için bu dökümanı takip edebilirsiniz.
Son derece etkileşimli ve dinamik bir web uygulaması söz konusu olduğunda Javascripti bu şekilde çalıştırmak kötü bir kullanıcı deneyimi(user exprience) sunuyor.Bu problem Google tarafından, Google Map uygulaması webte açıldığı zaman farkedildi. Javascirpt performasını webte artırmak için, daha iyi bir yol bulalım dediler ve V8 motorunu çıkardılar.
Google Chrome dünden bugüne V8 motoru kullanıyor.Başlangıçta, Javacript performansını iyileştirmek için, aşağıda gösterildiği gibi javascipt motorunun pipelinenına iki parça eklediler.
V8 motorunun 2010 versiyonunda, motorun ağır işlerini yapan iki ana parçası vardı. Full-codegenin işi, daha hızlı önyükleme(bootstrap) için mümkün olduğunca hızlı bir şekilde optimize edilmemiş makine kodlarını çıkaran baseline compilerdı.
Uygulama çalıştığı gibi, crankshaft compiler devreye girer.Kaynak kodu optimize eder ve baseline compiler tarafından oluşturulan makine kodlarının parçalarını değiştirir. Bu optimizasyon, daha iyi makine kodu üretildikçe daha iyi uygulama performansı ile sonuçlanacaktır.
Bununla birlikte, bu işlem, yüksek CPU kullanımına ve memory tüketimine sebep olur. Dolayısıyla V8 başka bir model bulması gerekiyordu.
Javascript motorunun yukarıdaki versiyonu bir interpreter içermiyor. Bu bir JIT(Just in time) compile modelidir. Kod anında makine koduna derlenir ve daha sonra makine koduna optimizasyon yapılır.
Just-in-Time Paradigması
Genel olarak kodunuzun çalışması için programlama dilinin makine koduna dönüştürülmesi gerekir. Bu dönüşümün nasıl ve ne zaman gerçekleşebileceğine dair bir kaç yaklaşım var.
Kodu dönüştürmenin en yaygın yolu aheadof-time compilation (AOT) yani vaktinden önce derleme işlemini gerçekleştirmektir. Tam olarak duyduğunuz gibi: Compile evresinde kod çalıştırılmadan önce kod makine koduna dönüştürülür.
C++,Java ve diğer birçok programlama dili bu yaklaşımı kullanır.
Diğer bir yaklaşımda, bir yorumlayıcı yani interpreter. Kodun her satırı runtimeda çalıştırılır. Bu yaklaşım genellikle Javascript ve Python gibi dinamik tipli diller tarafından benimsenir çünkü çalıştırılmadan önce tipleri tam olarak bilmek mümkün değildir.
AOT tüm kodu beraber değerlendirir, en iyi optimizasyonu sağlar ve sonunda daha yüksek performanslı bir kod üretir. Interpretation yani kodun yorumlanması daha basittir ancak genellikle compile etme seçeneğine göre daha yavaştır.
Dinamik yapılı diller için kodu daha hızlı ve daha etkili dönüştürmek için, Just-In-Time(JIT) adında yeni bir yaklaşım oluşturuldu. interpretation ve compilationun güçlü taraflarının bir kombini diyebiliriz
Temel yöntem olarak interpreter kullanılırken, V8 diğerlerinden daha sık kullanılan fonksiyonları algılayabilir ve önceki executionlardaki type bilgilerini kullanarak bunları compile edebilir.
Fakat type değişme ihtimali vardır. Compile olmuş kodu de-optimize etmemiz ve bunun yerine tekrar interpretere dönmemiz gerekiyor.( yeni type’ı aldıktan sonra fonksiyonu tekrar compile edebiliriz)
Javascript Nasıl Optimize Edildi?
Javascript kodunu optimize etmek için çeşitli kriterler var. Javascript kodu interpreter veya baseline compilera geçirilmeden önce, kodun ağaç benzeri bir yapısı olan Abstract Syntax Tree (AST)’na ayrıştırılması gerekir.
İlk olarak V8 kaynak kodunu indirir. Bu bir networkle,cachele veya service workerlarla yapılabilir.
Kod alınır alınmaz, compilerın anlayacağı şekilde değiştirilir. Bu işleme parsing adı verilir ve iki bölümden oluşur: scanner(tarayıcı) ve parser(ayrıştırıcı).
Scanner JS dosyasını alır ve onu tokenlara dönüştürür. keyword.txt dosyasında tüm tokenların bir listesi tutulur.
Parser kodu alır ve bir Abstract Syntax Tree oluşturur: kaynak kodunu temsil eden bir ağaç. Ağacın her düğümü kodda meydana gelen bir yapıyı belirtir.
Daha basit bir ifadeyle şu örneğe bakalım:
function foo() {
let bar = 1;
return bar;
}
Bu kod aşağıdaki ağaç yapısını üretir:
Bu kodu preorder(root, left, right) dolaşma ile çalıştırır:
- foo fonksiyonu tanımla
- bar değişkeni tanımla
- bar değişkenine 1 değerini ver
- bar değişkeninin değerini fonksiyonun dışına döndür
Bir Javascript uygulaması çalıştırdığımızda, uygulamanın başlangıcında tüm koda ihtiyacımız yok. Örneğin, kullanıcı etkileşime girdiğinde çalışacak bir fonksiyonumuz(buton click) var diyelim, bu kod daha sonra ayrıştırılabilir.
Anında ayrıştırılması gereken şeyleri belirlemek ve makine kodunu oluşturmak, daha hızlı bir uygulama önyüklemesi(bootstrap) için en iyi stratejidir.
Javascript motorunun daha az optimize edilmiş makine kodu üretmesini sağlayan şey, daha önce söylediğimiz gibi Javasripte tip tanımlamasının olmamasıdır. Bu nedenle önceden tanımlanmış değerleri temel alan bir javascript motoru, değişkenlerin veri tiplerini tahmin edebilir ve daha iyi makine kodu oluşturabilir.
Tüm bu süreç Paul Ryan tarafından alligator.io daki V8 motoru üzerine yazdığı blog yazısında dökümante edilmiş ve çok iyi açıklanmıştır. Bu konsepti derinlemesine anlamak için mutlaka bu yazıyı okumanızı öneririm.
Bu arada, Javascript motorunun yapacağı şey kod çalıştırken profile datalarını toplamak ve daha yavaş çalışan kodu aramaktır. Bu kod belki CPU’yu yaktığı içindir bilinmez HOT kod olarak bilinir. Bu kod dahada optimize edilebilir ve optimize edilmiş bir makine kodu ile değiştirilebilir.
Bu şeyleri ve full-codegen ile crankshaftdan kaynaklanan sorunları gözönünde bulundurarak V8 takımı sıfırdan V8 motorunun yeni bir sürümünü yarattı. Javascriptin bu yeni versiyonu 2017 de yayınlandı.
Yukarıdaki görseldede görüldüğü üzere, V8 ekibi, görevi baseline compiler kullanarak Javascript kaynak kodundan bytecode oluşturmak ve daha sonra bu bytecode’unu bir interpreter kullanarak makine koduna(binarycode) dönüştüren yeni bir interpreter pipeline’ ı olan Ignition’ı tanıttı.
Turbofan optimizasyon compileri, uygulama çalışırken bu bytecodeları arkaplanda( ayrı bir threadde) optimize edebilir ve sonunda değiştirilecek olan çok daha optimize edilmiş binarycode yani makine kodu oluşturabilir.
Turbofan profiling dataları Ignition interpreterınden alır ve HOT kodları arar. Kodun nasıl daha iyi optimize edileceğine dair tahminler yapabilir( veri türlerini tahmin ederek) ve kodu optimize veya de-optimize edebilir.
Diğer Javascipt motorları
V8 Javascript motorunun nasıl çalıştığına dair biraz konuştuk. Benzer modelleri diğer motorlarda uygulamaktadır. Firefox’un motoru SpiderMonkey ve Internet Explorer’ın motoru Chakra.
Bazı javascirpt motorları belki birden çok baseline ve optimizasyon compilerlarına sahip oldukları için karmaşık görünebilir. Fakat hepsi aynı optimizasyon modelini uygular.
V8 en populer Javascript motorlarından biridir. Belki Google tarafından geliştirildiği içindir. Fakat V8 motoru sürekli gelişiyor ve daha hızlı hale geliyor.Google chrome’dan ayrı olarak, Chromium, Electronjs ve server-side Javascript Nodejs V8 motorunu kullanır.
Microsoft edge browserı Chromium tabanlı sunmayı planlıyor.
Javascript Runtime
Javascript diğer dillerin aksine anlaması çok kolay ve front-end ekosisteminin temelidir.
Fakat diğer programalama dillerinin aksine runtime’da single-thread bir dildir. Bunun anlamı kod executionunda her seferinde kodun bir parçası çalışır.Code execution sıralı gerçekleştiğinden, yürütülmesi uzun zaman alan herhangi bir kod, bundan sonra yürütülmesi gereken her şeyi engelleyecektir. Bundan dolayı Google Chrome kullanırken bazen aşağıdaki gibi bir ekranla karşılaşabilirsiniz.
Browserda bir websayfası açtığın zaman, tek bir Javascript execution threadi kullanılır. Bu thread her şeyi handle etmekle sorumludur.Örneğin web sayfasını scroll etmek, web sayfasını yazdırmak, DOM eventlerini dinlemek (kullanıcın butona basması gibi), ve yapılan diğer tüm işlemler.
Fakat Javascript execution bloklandığı zaman, bu şeylerin hepsini yapmayı durdurur. Bu şu demek tarayıcının donacağı ve bu görev tamamlanana kadar hiçbir şeye yanıt vermeyeceği anlamına gelir.
Aşağıdaki kodu tarayıcında çalıştırıp görebilirsin.
while(true){}
Yukarıdaki döngüden sonraki herhangi bir kod, sistem kaynakları tükenene kadar while döngüsü sonsuz döngü yapacağından çalıştırılmayacaktır. Bu sonsuz recursive bir fonksiyon içinde geçerli.
Modern browserlar sayesinde, tüm açık browser sekmeleri tekbir Javascirpt threadine dayanmamaktadır. Bunun yerine her sekme veya her domain ayrı bir Javascript threadi kullanır. Google Chrome söz konusu olduğunda, farklı web siteleriyle birden çok sekme açabilir ve her sekmede sonsuz while döngüsünü çalıştırabilirsiniz.
Sadece kodun çalıştırıldığı aktif sekme donacaktır, diğer sekmeler işlerine devam edecektir. Herhangi bir sekme aynı domainde açılmışsa yani aynı site açıksa, Chrome one-process-per-site politikası uyguladığından ve process aynı Javascript execution threadini kullandığından sekme donacaktır.
Javascriptin bir programı nasıl çalıştırdığını görselleştirmek için Javascript çalışma zamanını ve içinde rol oynayan farklı birleşenleri anlamamız gerekir. Bunu görsel şekilde göstermek için basit bir program yazalım.
Burada foo, bar ve baz adında 3 tane fonksiyonumuz var.foo fonksiyonu bar fonksiyonunu, bar fonksiyonuda baz fonksiyonunu çağırıyor.Baz fonksiyonumuzda console’a bir şeyler yazıyor.
Ben programı çalıştırdığım zaman, ilk olarak foo çağrılır ve sonra call zinciri, console.log() çalıştırılana kadar devam eder. Bunu bir şema kullanarak görselleştirelim
Diğer programlama dillerindeki gibi, Javascript runtime bir stack ve bir heap depolama alanına sahip. Heap belleği rastgele sırayla saklayabileceğiniz boş bir bellek depolama birimidir. Önemli bir süre boyunca kalacak veriler heap’in içine girer. Heap Javascript runtime tarafından yönetilir ve garbage collector tarafından temizlenir. Heap hakkında çok fazla yazmayacağım burdan daha detaylı bilgi edinebilirsiniz.
Asıl bizi ilgilendiren kısım stack. Stack programın aktif çalışan fonksiyonun contextini saklayan ve LIFO(Last in Firs out) mantığında çalışan memory türüdür.Yukarıdaki programımız memoriye yüklendiğinde, ilk fonksiyondan yani foo() dan başlar.
Bundan dolayı stacke ilk giren foo() fonksiyonudur. foo fonksiyonu bar fonksiyonunu çağırdığı için stacke ikinci giren bar() fonksiyonudur. bar() fonksiyonuda baz fonksiyonunu çağırdığından stacke 3.giren fonksiyonda baz fonksiyonudur. Ve en sonunda baz() fonksiyonu console.log’u çağırır stacke 4.girende console.log(‘Hello from baz’) dır.
Bir fonksiyon bir şey döndürene kadar (fonksiyon çalışırken) stackten dışarı çıkmayacaktır.Bu fonksiyon bir değer döndürür döndürmez stack fonksiyonları tek tek çalıştıracak ve beklemede olan fonksiyonları çalıştırmaya devam edecektir.
Stackteki her entrye stack frame denir. Stack frame fonksiyonun local variableları veya fonksiyonun parametreleri, geri dönüş adresi gibi ve fonksiyonun diğer bilgilerini barındırır.
Yukarıdaki ekran görüntüsünde göreceğiniz üzere, console.log fonksiyonun olduğu satıra breakpoint eklerseniz Chrome DevTools bize Call Stack(sağ tarafta) gösterir. Breakpoint koyduğumuz fonksiyon çalışana kadar Call Stacke atılan stackframelerini görüntüler.
Eğer stack framedeki herhangi bir fonksiyon hata üretirse, bu hata üretilene kadarki stack frame in snapshot’unu console’a basar. Buna stack trace denir.
Yukarıdaki programda baz fonksiyonunun içerisinde hata fırlatır. Bu yüzden Javascript hata ile karşılaştığı zaman, console’a bir stack trace basar neyin yanlış gittiğini ve nerde yanlış birşeyler olduğunu gösterir.
Eğer javascriptin stack tracki hakkında daha fazla bilgi edinmek isterseniz ve daha fazla neler yaptığını görmek isterseniz V8'in bu yazısını okumanızı öneririm.
Javascript single thread olduğundan process başına bir heap ve bir yığın vardır. Bundan dolayı başka herhangi bir program bir şeyi çalıştırmak isterse, önceki programın tamamen çalıştırılmasını beklemek zorundadır. Bu thread genellikle main thread yada main execution thread olarak bilinir.
Şimdi şöyle bir senaryo düşünelim.Bir browser network üzerinden bazı verileri yüklemek veya web sayfasında görüntülemek üzere bir görüntü yüklemek için bir HTTP isteği gönderirse ne olur? Browser bu istek yanıt alana kadar donacak mı? Eğer donarsa, o zaman kullanıcı deneyimi açısından kötü olurdu.
Browser web sayfası içerisindeki javascripti çalıştırma sorumluluğu üstlenen bir javascript motoru ile gelir. Örneğin Google Chrome V8 motoru kullanır.
Fakat tahmin edeceğiniz üzere browser javascript motorunu kullanmanın ötesinde farklı işlerde yapar.Browserın kaputunu açarsak şöyle bir manzara çıkıyor.
Çok karmaşık görünüyor ama eğer her bir parçanın birbirleri ile nasıl bir harmonide çalıştığını anlarsanız o kadar karmaşık gelmeyecektir. Javascript runtime aslında 2 bileşenede daha sahip. event loop ve callback queue. Callback queue message queue veya task queue olarak adlandırılır.
Javascript motorununda ayrı olarak, browser çeşitli uygulamarı içerir. HTTP requesti atmak gibi, DOM eventlerini dinlemek gibi, setTimeout ve setInterval kullanarak executiona timer koymak gibi, cacheleme, veritabanı depolama ve daha fazlası. Browserın bu uygulamarı bize zengin web uygulamaları oluşturmamızı sağlar.
Fakat az önce bahsettiğimiz olayı düşünürsek, eğer browser tasklerin çalıştırılması için aynı Javascript threadini kullanırsa, o zaman kötü bir kullanıcı deneyimi sunarız. Örneğin tarayıcının HTTP response’u alındığında bir görevi gerçekleştirmek için aynı Javascript threadini kullanması gerekiyorsa, web sayfası saniyeler veya dakikalarca yanıt vermeyecektir.
Bundan dolayı browserın, HTTP requestleri gönderirken ve responseları dinlerken kendi logici vardır. Bu işlemler browser tarafından yönetilen farklı threadlerde ortaya çıktıkları için ve javascirptin bundan haberi olmadığı için Javascriptin main execution threadini bloklamaz.
Browser, performans avantajından dolayı bu özellikleri uygulamak için C/C++ gibi düşük seviyeli bir dil kullanabilir ve bu işlemleri Javascriptten yürütmemiz için bize temiz Javascript API’leri verir.Örneğin, fetch API browser tarafında bize HTTP requestleri göndermemizi sağlar.Bu API’ler Javascriptin bir parçası olmadığı için Web APIler olarak bilinir.
Bu Web APIleri asenkrondur. Yani bu APIlere geri planda birşeyler yapmaları ve iş tamamlandıktan sonra verileri döndürmeleri talimat verebileceğiniz anlamına gelir. Bu sırada biz javascripti kodunu bloklamamış oluyoruz. Bu API’lere arkaplanda birşeyler yapmalarını callback fonksiyonları ile sağlarız. Bu callback fonksiyonun sorumluluğu WEB API yapmakta olduğu işi tamamlar tamamlamaz ana Javascript threadin bazı javascript kodlarını çalıştırır.Biraz daha derine inelim.
Bir fonksiyonu çağrıldığında, bu işlem alınır ve stacke atılır. Eğer bu işlem Web API çağrısı içeriyorsa, Javascript bir callback fonksiyonu ile bunun denetimini Web APIsine devreder ve fonksiyon bir şey return edene kadar yani döndürene kadar sonraki satırları çalıştırmaya devam eder. Artık callback fonksiyonu main threadinden ayrı bir thread üzerinde işlemlerini gerçekleştiren Web APIsindedir.
Fonksiyon return ifadesine ulaştığında, bu fonksiyon stackten çıkar ve bir sonraki stack framene geçilir. Bu arada Web API arkaplanda işini yapıyor ve bu işle hangi callback fonksiyonunun ilgilendiğini biliyor.İşlem biter bitmez, Web API bu işin sonucunu callback fonksiyonuna bind ediyor ve mesaj queuesuna(diğer adıyla callback queue) callback fonksiyonu ile bir mesaj publish eder.
Event loop’un tek görevi callback queuesuna bakmak ve callback queuesunda bekleyen bir şey olduğunda, bu callbacki stacke atmaktır. Event loop stack boşaldığında stacke her seferinde bir callback fonksiyonu pushlar. Daha sonra stack callback fonksiyonunu çalıştırır.
setTimeout Web API kullanarak her şeyin adım adım nasıl çalıştığına bakalım. setTimeout Web API temel olarak bir kaç saniye sonra bir şeyi execute etmek için kullanılır. Bu execution programdaki tüm kodun çalışması bittiğinde gerçekleşir(yığın boş olduğunda). setTimeout syntaxı aşağıdaki gibidir.
setTimeout(callbackFunction, timeInMilliseconds);
callbackFunction 2.parametrede verilen timeInMilliseconds milisaniye sonra çalışacaktır. Daha önce yazdığımız programı aşağıdaki şekilde değiştirelim
Yaptığımız değişiklik printHello adında yeni bir fonksiyon tanımlayarak bunun çalıştırılma zamanın 3 saniye sonraya ayarlamak. Stack şu şekilde oluşmaya devam edecektir foo() => bar() => baz(). baz() fonksiyonunun çalışması başlar başlamaz setTimeout API çağrısı yapılacakır, Javascript callback fonksiyonunu Web API’ye vericektir ve sonraki satırdan çalışmaya devam edecektir.
Sonraki satır yoksa fonksiyon return edecek ve baz fonksiyonu stackten çıkarılacak.Sonrasında bar ve sonrasında foo fonksiyonları çağrılacaktır. Bu arada Web API 3 saniyenin geçmesini bekliyor. 3 saniye geçtiği an, callbacki callback queue’suna pushlayacak ve stack boş olduğu için, event loop bu callbacki, bu callback fonksiyonunun execute olacağı yığına geri koyacaktır.
Philip Robers Javacriptin nasıl çalıştığını görselleştirmek için harika bir çevrimiçi araç yapmış. Yukarıdaki örneğe bu linkten erişebilirsiniz.
Event loop ve callback queue aynı yapbozun parçaları gibi. Onlar javascript motorunun bir parçası değiller. Javascript motorunun dışında bir browser veya Nodejs gibi bir runtime tarafından sağlanan yapılardır. Event loop Javascript motoru ile motorun APIları sayesinde iletişim kurar ve execute edilecek callback fonksiyonlarını sağlar.
Node.js içerisinde Event Loop
Bir browserda arkaplanda yapacaklarımız sınırlıdır. Fakat node’da basit bir javascript programı olsa bile, hemen hemen arka planda çoğu şeyi yapabiliriz. Peki node.js nasıl çalışır.
Nodejs javascript runtime’ı sağlayan Goole’ın V8 motorunu kullanır ve C dilinde yazılan libuv adında kütüphaneyi kullanarak kendi event loopunu kullanır. Nodejs Web APIler gibi aynı callback yaklaşımını izler ve browsera benzer şekilde çalışır.
Eğer browser diyagramı ile node diyagramını karşılaştırırsanız, benzerlikleri görebilirsiniz. Sağdaki bölüm Web API’sine benzer, Ek olarak event queue(message/callback queue) ve event loopuda içerir.
Fakat V8, event queue ve event loop single threadde çalışırken, worker threadler asenkron I/O işlemi sağlamaktan sorumludur. Bu yüzden, Nodejs non-blocking event driven asenkron I/O mimarisine sahiptir.
Yukarıdaki konuları Philip Roberts 30 dakikalık videosunda anlatıyor. İzleminizi öneririm