Memahami Lingkaran Peristiwa

135

Saya memikirkannya dan inilah yang saya pikirkan:

Katakanlah kita memiliki kode seperti ini:

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

Permintaan masuk, dan mesin JS mulai mengeksekusi kode di atas langkah demi langkah. Dua panggilan pertama adalah panggilan sinkronisasi. Tetapi ketika datang ke setTimeoutmetode, itu menjadi eksekusi async. Tetapi JS segera kembali darinya dan melanjutkan eksekusi, yang disebut Non-Blockingatau Async. Dan itu terus bekerja pada yang lain dll.

Hasil dari eksekusi ini adalah sebagai berikut:

acdb

Jadi pada dasarnya yang kedua setTimeoutdiselesaikan terlebih dahulu dan fungsi callback dijalankan lebih awal dari yang pertama dan itu masuk akal.

Kita berbicara tentang aplikasi single-threaded di sini. JS Engine terus menjalankan ini dan kecuali itu menyelesaikan permintaan pertama, itu tidak akan pergi ke yang kedua. Tetapi hal baiknya adalah bahwa ia tidak akan menunggu untuk memblokir operasi yang ingin setTimeoutdiselesaikan sehingga akan lebih cepat karena menerima permintaan masuk yang baru.

Tetapi pertanyaan saya muncul di sekitar item berikut:

# 1: Jika kita berbicara tentang aplikasi single-threaded, lalu mekanisme apa yang diproses setTimeoutssaat mesin JS menerima lebih banyak permintaan dan menjalankannya? Bagaimana utas tunggal terus bekerja pada permintaan lain? Apa yang berfungsi setTimeoutsaat permintaan lainnya terus masuk dan dieksekusi.

# 2: Jika setTimeoutfungsi - fungsi ini dieksekusi di belakang layar saat lebih banyak permintaan masuk dan dieksekusi, apa yang melakukan eksekusi async di belakang layar? Benda apa yang kita bicarakan ini disebut EventLoop?

# 3: Tapi bukankah seluruh metode harus dimasukkan EventLoopsehingga semuanya dieksekusi dan metode panggilan balik dipanggil? Inilah yang saya mengerti ketika berbicara tentang fungsi callback:

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

Tetapi dalam kasus ini, bagaimana JS Engine tahu jika itu adalah fungsi async sehingga dapat menempatkan panggilan balik dalam EventLoop? Perhaps something like thekata kunci async` dalam C # atau semacam atribut yang menunjukkan metode yang akan diambil oleh Mesin JS adalah metode async dan harus diperlakukan sebagaimana mestinya.

# 4: Tapi sebuah artikel mengatakan sangat bertentangan dengan apa yang saya duga tentang bagaimana segala sesuatunya bekerja:

Loop Peristiwa adalah antrian fungsi panggilan balik. Ketika fungsi async dijalankan, fungsi callback didorong ke antrian. Mesin JavaScript tidak mulai memproses loop acara hingga kode setelah fungsi async dijalankan.

# 5: Dan ada gambar ini di sini yang mungkin bisa membantu tetapi penjelasan pertama dalam gambar mengatakan hal yang persis sama yang disebutkan dalam pertanyaan nomor 4:

masukkan deskripsi gambar di sini

Jadi pertanyaan saya di sini adalah untuk mendapatkan klarifikasi tentang barang-barang yang tercantum di atas?

Tarik
sumber
1
Utas bukan metafora yang tepat untuk menangani masalah itu. Pikirkan peristiwa.
Denys Séguret
1
@dystroy: Contoh kode untuk menggambarkan bahwa peristiwa metafora di JS akan menyenangkan untuk dilihat.
Tarik
Saya tidak mengerti apa sebenarnya pertanyaan Anda di sini.
Denys Séguret
1
@dystroy: Pertanyaan saya di sini adalah untuk mendapatkan beberapa klarifikasi tentang item yang tercantum di atas?
Tarik
2
Node bukan satu utas tetapi itu tidak masalah bagi Anda (terlepas dari fakta bahwa ia berhasil menyelesaikan hal-hal lain saat kode pengguna Anda dijalankan). Hanya satu panggilan balik paling banyak dalam kode pengguna Anda yang dieksekusi sekaligus.
Denys Séguret

Jawaban:

85

1: Jika kita berbicara tentang aplikasi single-threaded, lalu proses apa setTimeouts sementara mesin JS menerima lebih banyak permintaan dan menjalankannya? Bukankah itu utas tunggal akan terus bekerja pada permintaan lain? Lalu siapa yang akan terus bekerja di setTimeout sementara permintaan lainnya terus datang dan dieksekusi.

Hanya ada 1 utas dalam proses simpul yang benar-benar akan menjalankan JavaScript program Anda. Namun, di dalam node itu sendiri, sebenarnya ada beberapa thread yang menangani operasi dari mekanisme loop event, dan ini termasuk kumpulan thread IO dan beberapa lainnya. Kuncinya adalah jumlah utas ini tidak sesuai dengan jumlah koneksi konkurensi yang ditangani seperti yang akan mereka lakukan dalam model konkurensi thread-per-koneksi.

Sekarang tentang "mengeksekusi setTimeouts", ketika Anda memohon setTimeout, semua node tidak pada dasarnya memperbarui struktur data fungsi yang akan dieksekusi pada suatu waktu di masa depan. Ini pada dasarnya memiliki banyak antrian hal-hal yang perlu dilakukan dan setiap "centang" dari loop acara yang dipilihnya, menghapusnya dari antrian, dan menjalankannya.

Hal utama yang harus dipahami adalah simpul bergantung pada OS untuk sebagian besar pengangkatan berat. Jadi permintaan jaringan yang masuk sebenarnya dilacak oleh OS itu sendiri dan ketika node siap untuk menangani itu hanya menggunakan panggilan sistem untuk meminta OS untuk permintaan jaringan dengan data siap untuk diproses. Begitu banyak simpul "kerja" IO yang berfungsi sebagai "Hai OS, dapatkan koneksi jaringan dengan data yang siap dibaca?" atau "Hai OS, apakah panggilan sistem file saya yang luar biasa sudah siap data?". Berdasarkan algoritma internal dan desain mesin acara loop, node akan memilih satu "centang" dari JavaScript untuk dieksekusi, jalankan, lalu ulangi proses dari awal lagi. Itulah yang dimaksud dengan loop acara. Node pada dasarnya setiap saat menentukan "apa yang harus saya jalankan JavaScript selanjutnya," kemudian jalankan.setTimeoutatau process.nextTick.

2: Jika setTimeout ini akan dieksekusi di belakang layar sementara lebih banyak permintaan datang dan masuk dan dieksekusi, hal yang melaksanakan eksekusi async di belakang layar adalah bahwa yang kita bicarakan tentang EventLoop?

Tidak ada JavaScript yang dieksekusi di belakang layar. Semua JavaScript di program Anda berjalan di depan dan tengah, satu per satu. Apa yang terjadi di belakang layar adalah OS menangani IO dan node menunggu untuk siap dan node mengelola antrian javascript yang menunggu untuk dieksekusi.

3: Bagaimana JS Engine dapat mengetahui apakah ini merupakan fungsi async sehingga dapat menempatkannya di EventLoop?

Ada satu set fungsi tetap dalam inti simpul yang async karena mereka membuat panggilan sistem dan simpul tahu yang mana ini karena mereka harus memanggil OS atau C ++. Pada dasarnya semua jaringan dan sistem file IO serta interaksi proses anak akan tidak sinkron dan satu-satunya cara JavaScript bisa mendapatkan node untuk menjalankan sesuatu secara tidak sinkron adalah dengan memanggil salah satu fungsi async yang disediakan oleh perpustakaan inti node. Bahkan jika Anda menggunakan paket npm yang mendefinisikan API itu sendiri, untuk menghasilkan loop peristiwa, akhirnya kode paket npm akan memanggil salah satu fungsi async inti node dan saat itulah simpul tahu centang sudah selesai dan dapat memulai acara algoritma loop lagi.

4 Loop Acara adalah antrian fungsi panggilan balik. Ketika fungsi async dijalankan, fungsi callback didorong ke antrian. Mesin JavaScript tidak mulai memproses loop acara hingga kode setelah fungsi async dijalankan.

Ya, ini benar, tetapi menyesatkan. Kuncinya adalah pola normal adalah:

//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

Jadi ya, Anda benar-benar dapat memblokir loop peristiwa dengan hanya menghitung angka Fibonacci secara bersamaan semua dalam memori semua dalam centang yang sama, dan ya itu benar-benar akan membekukan program Anda. Ini konkurensi kerja sama. Setiap centang JavaScript harus menghasilkan loop peristiwa dalam jumlah waktu yang wajar atau keseluruhan arsitektur gagal.

Peter Lyons
sumber
1
Katakanlah saya memiliki antrian yang akan mengambil server 1 menit untuk mengeksekusi, dan hal pertama adalah beberapa fungsi async yang selesai setelah 10 detik. Apakah akan pergi ke akhir antrian atau akan mendorong dirinya sendiri ke garis segera setelah siap?
ilyo
4
Secara umum ini akan menuju akhir antrian, tetapi semantik dari process.nextTickvs setTimeoutvs setImmediateagak berbeda, meskipun Anda tidak harus benar-benar peduli. Saya memiliki posting blog yang disebut setTimeout dan teman - teman yang lebih detail.
Peter Lyons
Bisakah Anda jelaskan? Katakanlah saya memiliki dua panggilan balik dan yang pertama memiliki metode changeColor dengan waktu eksekusi 10mS dan setTimeout 1 menit dan yang kedua memiliki metode changeBackground dengan waktu eksekusi 50ms dengan setTimeout 10 detik. Saya merasakan changeBackground dengan berada di Antrean terlebih dahulu dan changeColor akan menjadi yang berikutnya. Setelah itu loop Acara memilih metode secara sinkron. Apakah saya benar?
SheshPai
1
@SheshPai terlalu membingungkan bagi semua orang untuk membahas kode ketika ditulis dalam paragraf bahasa Inggris. Cukup posting pertanyaan baru dengan potongan kode sehingga orang dapat menjawab berdasarkan kode alih-alih deskripsi kode, yang menyisakan banyak ambiguitas.
Peter Lyons
youtube.com/watch?v=QyUFheng6J0&spfreload=5 ini adalah penjelasan lain yang baik dari JavaScript Engine
Mukesh Kumar
65

Ada tutorial video yang fantastis oleh Philip Roberts, yang menjelaskan loop peristiwa javascript dengan cara yang paling sederhana dan konseptual. Setiap pengembang javascript harus melihatnya.

Berikut ini tautan video di Youtube.

sam100rav
sumber
16
Saya menontonnya dan itu memang penjelasan terbaik yang pernah ada.
Tarik
1
Harus menonton video untuk penggemar dan penggemar JavaScript.
Nirus
1
video ini mengubah hidup saya ^^
HuyTran
1
datang berkeliaran di sini .. dan ini adalah salah satu penjelasan terbaik yang saya dapatkan .. terima kasih telah berbagi ...: D
RohitS
1
Itu adalah
pembuka mata
11

Jangan mengira proses host menjadi single-threaded, mereka tidak. Apa yang single-threaded adalah bagian dari proses host yang mengeksekusi kode javascript Anda.

Kecuali untuk pekerja latar belakang , tetapi ini menyulitkan skenario ...

Jadi, semua kode js Anda berjalan di utas yang sama, dan tidak ada kemungkinan bahwa Anda mendapatkan dua bagian berbeda dari kode js Anda untuk berjalan secara bersamaan (jadi, Anda tidak dapat mengelola concurrency nigthmare).

Kode js yang mengeksekusi adalah kode terakhir yang diambil oleh proses host dari loop acara. Dalam kode Anda, pada dasarnya Anda dapat melakukan dua hal: menjalankan instruksi yang sinkron, dan menjadwalkan fungsi yang akan dieksekusi di masa depan, ketika beberapa peristiwa terjadi.

Inilah representasi mental saya (berhati-hatilah: hanya saja, saya tidak tahu detail penerapan browser!) Dari kode contoh Anda:

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);       

Ketika kode Anda sedang berjalan, utas lain dalam proses host melacak semua peristiwa sistem yang terjadi (klik pada UI, membaca file, paket jaringan yang diterima dll.)

Ketika kode Anda selesai, itu dihapus dari loop acara, dan proses host kembali untuk memeriksanya, untuk melihat apakah ada lebih banyak kode untuk dijalankan. Event loop berisi dua event handler lebih: satu untuk dieksekusi sekarang (fungsi justNow), dan satu lagi dalam satu detik (fungsi inAWhile).

Proses host sekarang mencoba untuk mencocokkan semua peristiwa yang terjadi untuk melihat apakah ada penangan terdaftar untuk mereka. Ditemukan bahwa peristiwa yang baru saja ditunggu-tunggu telah terjadi, sehingga mulai menjalankan kodenya. Saat justNow keluar dari fungsi, ia memeriksa perulangan acara di lain waktu, menunjukkan adanya penangan pada acara. Andaikata 1 s telah lewat, ia menjalankan fungsi inAWhile, dan seterusnya ....

Andrea Parodi
sumber