Olay Döngüsünü Anlamak


135

Bunun hakkında düşünüyorum ve bulduğum şey şu:

Aşağıdaki kodu görelim:

console.clear();
console.log("a");
setTimeout(function(){console.log("b");},1000);
console.log("c");
setTimeout(function(){console.log("d");},0);

Bir istek gelir ve JS motoru yukarıdaki kodu adım adım çalıştırmaya başlar. İlk iki çağrı senkron çağrılardır. Ancak setTimeoutyöntem söz konusu olduğunda , eşzamansız bir yürütme haline gelir. Ancak JS hemen ondan geri döner ve Non-Blockingveya adı verilen çalıştırmaya devam eder Async. Ve diğerleri üzerinde çalışmaya devam ediyor.

Bu uygulamanın sonuçları şu şekildedir:

ACDB

Yani temelde ikincisi setTimeoutilk olarak bitirildi ve geri arama işlevi ilkinden daha erken yürütülüyor ve bu mantıklı.

Burada tek iş parçacıklı uygulamadan bahsediyoruz. JS Engine, bunu yürütmeye devam eder ve ilk isteği bitirmediği sürece ikinciye gitmez. Ancak iyi olan şey, çözülmek gibi engelleme işlemlerini beklemeyeceği için setTimeoutyeni gelen istekleri kabul ettiği için daha hızlı olacaktır.

Ancak sorularım şu maddeler etrafında ortaya çıkıyor:

# 1: Tek iş parçacıklı bir uygulamadan bahsediyorsak setTimeouts, JS motoru daha fazla isteği kabul edip çalıştırırken hangi mekanizma işliyor? Tek iş parçacığı diğer istekler üzerinde nasıl çalışmaya devam ediyor? setTimeoutDiğer talepler gelip yerine getirilirken neler çalışıyor .

# 2: Daha setTimeoutfazla istek gelirken ve yürütülürken bu işlevler perde arkasında yürütülürse, perde arkasındaki eşzamansız yürütmeleri ne gerçekleştirir? Bu bahsettiğimiz şey nedir EventLoop?

# 3: Ancak EventLoop, her şeyin çalıştırılması ve geri çağırma yönteminin çağrılması için tüm yöntem yerleştirilmemeli mi? Geri arama işlevleri hakkında konuşurken anladığım şey bu:

function downloadFile(filePath, callback)
{
   blah.downloadFile(filePath);
   callback();
}

Ancak bu durumda, JS Engine, bir eşzamansız işlev olup olmadığını nasıl anlar, böylece geri aramayı EventLoop? Belki de asyncC #'daki anahtar kelime gibi bir şey veya JS Engine'in alacağı yöntemi gösteren bir tür öznitelik eşzamansız bir yöntemdir ve buna göre ele alınmalıdır.

4: Ancak bir makale , işlerin nasıl yürüdüğüne dair tahmin ettiğim şeyin tam tersini söylüyor:

Olay Döngüsü, geri arama işlevlerinin bir kuyruğudur. Eşzamansız bir işlev yürütüldüğünde, geri arama işlevi kuyruğa itilir. JavaScript motoru, eşzamansız bir işlevin ardından kod çalıştırılıncaya kadar olay döngüsünü işlemeye başlamaz.

# 5: Burada yardımcı olabilecek bir resim var ama resimdeki ilk açıklama 4. soruda bahsedilen şeyin aynısını söylüyor:

görüntü açıklamasını buraya girin

Yani buradaki sorum yukarıda listelenen maddeler hakkında bazı açıklamalar almak mı?


1
Konular, bu sorunları çözmek için doğru metafor değildir. Olayları düşünün.
Denys Séguret

1
@dystroy: JS'deki olay metaforunu görmek güzel olurdu.
Tarık

Burada tam olarak sorunun ne olduğunu anlamıyorum.
Denys Séguret

1
@dystroy: Buradaki sorum yukarıda listelenen öğeler hakkında bazı açıklamalar almak mı?
Tarık

2
Düğüm tek iş parçacıklı değildir, ancak sizin için önemli değildir (kullanıcı kodunuz yürütülürken başka şeyler yapmayı başardığı gerçeği dışında). Bir seferde kullanıcı kodunuzda en fazla bir geri arama yürütülür.
Denys Séguret

Yanıtlar:


85

1: Tek iş parçacıklı bir uygulamadan bahsediyorsak, JS motoru daha fazla isteği kabul edip çalıştırırken setTimeouts'u hangi işlemden geçirir? Bu tek iş parçacığı diğer istekler üzerinde çalışmaya devam etmeyecek mi? O halde, diğer istekler gelip yürütülürken kim setTimeout üzerinde çalışmaya devam edecek.

Düğüm işleminde, programınızın JavaScript'ini gerçekten çalıştıracak yalnızca 1 iş parçacığı vardır. Bununla birlikte, düğümün kendi içinde, olay döngüsü mekanizmasının işleyişini işleyen birkaç evre vardır ve bu, bir IO iş parçacığı havuzu ve bir avuç diğerleri içerir. Önemli olan, bu iş parçacığı sayısının bağlantı başına iş parçacığı eşzamanlılık modelinde olduğu gibi işlenen eşzamanlı bağlantı sayısına karşılık gelmemesidir.

Şimdi, çağırdığınızda "setTimeouts'un yürütülmesi" hakkında setTimeout , düğümün yaptığı her şey, temelde gelecekte bir zamanda yürütülecek işlevlerin veri yapısını güncellemektir. Temelde yapılması gereken bir sürü şey kuyruğuna sahiptir ve olay döngüsünün her "tık" sını seçer, kuyruktan kaldırır ve çalıştırır.

Anlaşılması gereken önemli bir nokta, düğümün ağır işlerin çoğu için işletim sistemine güvenmesidir. Dolayısıyla, gelen ağ istekleri aslında işletim sisteminin kendisi tarafından izlenir ve düğüm birini işlemeye hazır olduğunda, işletim sisteminden işlenmeye hazır verileri içeren bir ağ isteği istemek için bir sistem çağrısı kullanır. GÇ "çalışma" düğümünün çoğu "Hey OS, okunmaya hazır veriler içeren bir ağ bağlantınız mı var?" veya "Hey OS, olağanüstü dosya sistemi çağrılarımdan herhangi birinde veri hazır mı?" Düğüm, dahili algoritmasına ve olay döngüsü motoru tasarımına bağlı olarak, JavaScript'in çalıştırılması için bir "onay" ı seçer, çalıştırır ve ardından işlemi baştan tekrar eder. Olay döngüsü ile kastedilen budur. Düğüm temelde her zaman "çalıştırmam gereken bir sonraki JavaScript parçasının ne olduğunu" belirler ve sonra çalıştırır.setTimeout veya process.nextTick .

2: Bu setTimeout, daha fazla istek gelirken, gelirken ve yürütülürken perde arkasında yürütülürse, perde arkasındaki asenkron yürütmeleri gerçekleştiren şey, EventLoop'tan bahsettiğimiz şey mi?

Perde arkasında hiçbir JavaScript çalıştırılmaz. Programınızdaki tüm JavaScript, birer birer, önde ve ortada çalışır. Perde arkasında olan şey, işletim sisteminin GÇ'yi yönetmesidir ve düğüm bunun hazır olmasını bekler ve düğüm, yürütmeyi bekleyen javascript kuyruğunu yönetir.

3: JS Engine, EventLoop'a koyabilmek için asenkron bir işlev olup olmadığını nasıl bilebilir?

Düğüm çekirdeğinde sabit bir dizi işlev vardır, çünkü bunlar sistem çağrıları yaparlar ve düğüm bunların hangilerini OS veya C ++ çağırmak zorunda olduklarını bilir. Temel olarak tüm ağ ve dosya sistemi GÇ'si ve alt süreç etkileşimleri eşzamansız olacaktır ve JavaScript'in bir şeyi eşzamansız olarak çalıştırmak için düğümü almasının YALNIZCA yolu, düğüm çekirdek kitaplığı tarafından sağlanan eşzamansız işlevlerden birini çağırmaktır. Olay döngüsünü sağlamak için kendi API'sini tanımlayan bir npm paketi kullanıyor olsanız bile, nihayetinde npm paketinin kodu düğüm çekirdeğinin eşzamansız işlevlerinden birini çağıracaktır ve bu noktada düğüm, tick'in tamamlandığını anlar ve olayı başlatabilir. tekrar döngü algoritması.

4 Olay Döngüsü, bir geri arama işlevleri kuyruğudur. Eşzamansız bir işlev yürütüldüğünde, geri arama işlevi kuyruğa itilir. JavaScript motoru, eşzamansız bir işlevin ardından kod çalıştırılana kadar olay döngüsünü işlemeye başlamaz.

Evet, bu doğru ama yanıltıcı. Önemli olan normal model şudur:

//Let's say this code is running in tick 1
fs.readFile("/home/barney/colors.txt", function (error, data) {
  //The code inside this callback function will absolutely NOT run in tick 1
  //It will run in some tick >= 2
});
//This code will absolutely also run in tick 1
//HOWEVER, typically there's not much else to do here,
//so at some point soon after queueing up some async IO, this tick
//will have nothing useful to do so it will just end because the IO result
//is necessary before anything useful can be done

Yani evet, tüm Fibonacci sayılarını eşzamanlı olarak bellekte sayarak olay döngüsünü tamamen engelleyebilirsiniz ve evet, bu programınızı tamamen dondurur. İşbirliğine dayalı eşzamanlılık. Her JavaScript tıklaması, makul bir süre içinde olay döngüsünü vermelidir, aksi takdirde genel mimari başarısız olur.


1
Sunucunun yürütülmesi 1 dakika sürecek bir kuyruğum olduğunu varsayalım ve ilk şey 10 saniye sonra biten bir eşzamansız işlevdi. Kuyruğun sonuna mı gidecek yoksa hazır olduğu anda kendisini çizgiye mi itecek?
ilyo

4
Genellikle kuyruğun sonuna kadar gider, ancak semantiği process.nextTick vs setTimeoutvs setImmediategerçekten bakım olmamalıdır rağmen, ustaca farklıdır. SetTimeout ve daha fazla ayrıntıya giren arkadaşlar adında bir blog yazım var .
Peter Lyons

Lütfen detaylandırır mısınız? Diyelim ki iki geri çağrım var ve birincisinin yürütme süresi 10mS ve setTimeout olan bir changeColor yöntemi ve ikincisi 10 saniyelik setTimeout ile 50ms yürütme süresine sahip bir changeBackground yöntemine sahip. Önce Queue'da olan changeBackground'un ve bir sonraki changeColor'un olacağını hissediyorum. Bundan sonra, Olay döngüsü yöntemleri eşzamanlı olarak seçer. Haklı mıyım
SheshPai

1
@SheshPai, İngilizce paragraflarda yazıldığında herkes için kod tartışmak için çok kafa karıştırıcı. Bir kod parçacığı ile yeni bir soru göndermeniz yeterlidir, böylece insanlar kod açıklaması yerine koda göre cevap verebilir, bu da çok fazla belirsizlik bırakır.
Peter Lyons

youtube.com/watch?v=QyUFheng6J0&spfreload=5 bu, JavaScript Motorunun başka bir iyi açıklamasıdır
Mukesh Kumar

65

Philip Roberts'ın javascript olay döngüsünü en basit ve kavramsal şekilde açıklayan harika bir video eğitimi var. Her javascript geliştiricisinin bir bakması gerekir.

İşte Youtube'daki video bağlantısı .


16
Onu izledim ve gerçekten de şimdiye kadarki en iyi açıklamaydı.
Tarık

1
JavaScript hayranları ve meraklıları için mutlaka izlenmeli video.
Nirus

1
bu video benim canlı
yayımı

1
burada dolaşıp geldi .. ve bu sahip olduğum en iyi açıklamalardan biri .. paylaştığın için teşekkürler ...: D
RohitS

1
Bu bir göz açıcıydı
HebleV

11

Ana makine sürecinin tek iş parçacıklı olduğunu düşünmeyin, öyle değil. Tek iş parçacıklı olan, ana bilgisayar işleminin javascript kodunuzu çalıştıran kısmıdır.

Arka plandaki çalışanlar hariç , ama bunlar senaryoyu karmaşıklaştırıyor ...

Bu nedenle, tüm js kodunuz aynı iş parçacığı içinde çalışır ve js kodunuzun aynı anda çalışması için iki farklı bölümünü elde etme olasılığınız yoktur (bu nedenle, eşzamanlılık nigthmare'yi yönetemezsiniz).

Yürütülen js kodu, ana bilgisayar işleminin olay döngüsünden aldığı son koddur. Kodunuzda temel olarak iki şey yapabilirsiniz: eşzamanlı komutları çalıştırabilir ve gelecekte bazı olaylar olduğunda çalıştırılacak işlevleri programlayabilirsiniz.

İşte örnek kodunuzun zihinsel temsilim (dikkat: sadece bu, tarayıcı uygulama ayrıntılarını bilmiyorum!):

console.clear();                                   //exec sync
console.log("a");                                  //exec sync
setTimeout(                //schedule inAWhile to be executed at now +1 s 
    function inAWhile(){
        console.log("b");
    },1000);    
console.log("c");                                  //exec sync
setTimeout(
    function justNow(){          //schedule justNow to be executed just now
        console.log("d");
},0);       

Kodunuz çalışırken, ana makine sürecindeki başka bir iş parçacığı, meydana gelen tüm sistem olaylarını (kullanıcı arayüzüne tıklamalar, okunan dosyalar, alınan ağ paketleri vb.)

Kodunuz tamamlandığında, olay döngüsünden kaldırılır ve ana bilgisayar işlemi çalıştırılacak daha fazla kod olup olmadığını görmek için onu kontrol etmeye geri döner. Olay döngüsü iki olay işleyicisi içerir: biri şimdi çalıştırılacak (justNow işlevi) ve diğeri bir saniye içinde (inAWhile işlevi).

Ana bilgisayar süreci şimdi, onlar için kayıtlı işleyiciler olup olmadığını görmek için gerçekleşen tüm olayları eşleştirmeye çalışır. JustNow'un beklediği olayın gerçekleştiğini ve bu nedenle kodunu çalıştırmaya başladığını buldu. JustNow işlevi çıktığı zaman, olay döngüsünü başka bir zaman kontrol eder ve olayları işleyicileri araştırır. 1 s'nin geçtiğini varsayarsak, inAWhile fonksiyonunu çalıştırır ve böyle devam eder ....


SetTimeout, ana iş parçacığında gerçeklenir. Yani örneğinizde ayrı bir iş parçacığı gerektiren hiçbir şey yok. Aslında, tarayıcılarda yalnızca sekmeler birden çok iş parçacığında uygulanmaktadır. Tek bir sekmede, birden çok paralel ağ bağlantısı yapmak, fare tıklamalarını beklemek, setTimeout, animasyonlar vb. Dahil tüm işlemler aynı iş parçacığında yapılır
slebetman
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.