Rumah >hujung hadapan web >tutorial js >Penjelasan terperinci tentang gelung peristiwa mekanisme pengendalian JavaScript (Gelung Acara)_kemahiran javascript
1. Mengapakah JavaScript berbenang tunggal?
Ciri utama bahasa JavaScript ialah single-threading, yang bermaksud bahawa ia hanya boleh melakukan satu perkara pada satu masa. Jadi mengapa JavaScript tidak boleh mempunyai berbilang benang? Ini boleh meningkatkan kecekapan.
Urut tunggal JavaScript berkaitan dengan tujuannya. Sebagai bahasa skrip pelayar, tujuan utama JavaScript adalah untuk berinteraksi dengan pengguna dan memanipulasi DOM. Ini menentukan bahawa ia hanya boleh menjadi satu-benang, jika tidak, ia akan menyebabkan masalah penyegerakan yang sangat kompleks. Sebagai contoh, katakan JavaScript mempunyai dua utas pada masa yang sama Satu utas menambah kandungan pada nod DOM tertentu, dan utas lain memadamkan nod tersebut.
Oleh itu, untuk mengelakkan kerumitan, JavaScript telah menjadi satu utas sejak ia dilahirkan Ini telah menjadi ciri teras bahasa dan tidak akan berubah pada masa hadapan.
Untuk memanfaatkan kuasa pengkomputeran CPU berbilang teras, HTML5 mencadangkan standard Pekerja Web, yang membenarkan skrip JavaScript membuat berbilang rangkaian, tetapi urutan anak dikawal sepenuhnya oleh utas utama dan tidak boleh mengendalikan DOM. Oleh itu, piawaian baharu ini tidak mengubah sifat berbenang tunggal JavaScript.
2. Barisan Tugasan
Urut tunggal bermakna semua tugasan perlu dibariskan, dan tugasan seterusnya tidak akan dilaksanakan sehingga tugasan sebelumnya selesai. Jika tugasan sebelumnya mengambil masa yang lama, tugasan seterusnya perlu menunggu.
Jika baris gilir disebabkan oleh jumlah pengiraan yang banyak dan CPU terlalu sibuk, lupakan, tetapi banyak kali CPU melahu kerana peranti IO (peranti input dan output) sangat perlahan (seperti operasi Ajax untuk baca data dari rangkaian) , kena tunggu keputusan keluar sebelum meneruskan.
Pereka bahasa JavaScript menyedari bahawa pada masa ini, CPU boleh mengabaikan sepenuhnya peranti IO, menggantung tugas menunggu dan menjalankan tugas kemudiannya terlebih dahulu. Tunggu sehingga peranti IO mengembalikan hasilnya, kemudian kembali dan teruskan melaksanakan tugas yang digantung.
Akibatnya, JavaScript mempunyai dua kaedah pelaksanaan: satu ialah CPU melaksanakannya dalam urutan, tugasan sebelumnya tamat, dan kemudian tugasan seterusnya dilaksanakan. Ini dipanggil pelaksanaan segerak; dengan masa menunggu yang lama , proses tugasan berikutnya dahulu, yang dipanggil pelaksanaan tak segerak. Terpulang kepada pengaturcara untuk memilih kaedah pelaksanaan yang akan digunakan.
Secara khusus, mekanisme pengendalian pelaksanaan tak segerak adalah seperti berikut. (Begitu juga dengan pelaksanaan segerak, kerana ia boleh dianggap sebagai pelaksanaan tak segerak tanpa tugas tak segerak.)
(1) Semua tugasan dilaksanakan pada utas utama, membentuk tindanan konteks pelaksanaan.
(2) Sebagai tambahan kepada utas utama, terdapat juga "gilir tugas". Sistem meletakkan tugas tak segerak ke dalam "baris gilir tugas" dan kemudian terus melaksanakan tugasan berikutnya.
(3) Setelah semua tugasan dalam "timbunan pelaksanaan" telah dilaksanakan, sistem akan membaca "gilir tugas". Jika pada masa ini, tugas tak segerak telah menamatkan keadaan menunggu, ia akan memasuki timbunan pelaksanaan daripada "gilir tugas" dan menyambung semula pelaksanaan.
(4) Benang utama terus mengulangi langkah ketiga di atas.
Gambar di bawah ialah gambarajah skematik urutan utama dan baris gilir tugas.
Selagi utas utama kosong, ia akan membaca "baris gilir tugas". Ini adalah mekanisme berjalan JavaScript. Proses ini terus berulang.
3. Acara dan fungsi panggil balik
"Baris gilir tugas" pada asasnya ialah baris gilir acara (juga boleh difahami sebagai baris gilir mesej apabila peranti IO menyelesaikan tugas, acara ditambahkan pada "baris gilir tugas" untuk menunjukkan bahawa tugas tak segerak yang berkaitan boleh masuk" Timbunan pelaksanaan". Benang utama membaca "baris gilir tugas", yang bermaksud membaca peristiwa di dalamnya.
Acara dalam "Baris Gilir Tugas", sebagai tambahan kepada acara peranti IO, juga termasuk beberapa acara yang dijana pengguna (seperti klik tetikus, menatal halaman, dll.). Selagi fungsi panggil balik ditentukan, peristiwa ini akan memasuki "baris gilir tugas" apabila ia berlaku, menunggu urutan utama dibaca.
Apa yang dipanggil "fungsi panggil balik" ialah kod yang akan digantung oleh utas utama. Tugas tak segerak mesti menentukan fungsi panggil balik Apabila tugas tak segerak kembali daripada "baris gilir tugas" ke timbunan pelaksanaan, fungsi panggil balik akan dilaksanakan.
"Baris gilir tugas" ialah struktur data masuk dahulu, keluar dahulu Acara yang berada di kedudukan pertama dikembalikan ke urutan utama terlebih dahulu. Proses membaca utas utama pada asasnya adalah automatik Sebaik sahaja timbunan pelaksanaan dikosongkan, acara pertama pada "gilir tugas" akan kembali ke utas utama secara automatik. Walau bagaimanapun, disebabkan oleh fungsi "pemasa" yang dinyatakan kemudian, utas utama perlu menyemak masa pelaksanaan, dan acara tertentu mesti kembali ke utas utama pada masa yang ditentukan.
4. Gelung Acara
Urut utama membaca peristiwa daripada "baris gilir tugasan".
Untuk lebih memahami Gelung Acara, sila lihat gambar di bawah (dipetik daripada ucapan Philip Roberts "Bantuan, saya terperangkap dalam gelung acara").
Dalam gambar di atas, apabila utas utama sedang berjalan, timbunan dan timbunan dijana Kod dalam timbunan memanggil pelbagai API luaran, dan mereka menambah pelbagai acara (klik, muatkan, dll.) pada "tugas. beratur". selesai). Selagi kod dalam timbunan dilaksanakan, utas utama akan membaca "baris gilir tugas" dan melaksanakan fungsi panggil balik yang sepadan dengan peristiwa tersebut dalam urutan.
Kod dalam timbunan pelaksanaan sentiasa dilaksanakan sebelum membaca "gilir tugas". Sila lihat contoh di bawah.
setTimeout() menerima dua parameter, yang pertama ialah fungsi panggil balik dan yang kedua ialah bilangan milisaat untuk menangguhkan pelaksanaan.
Salin kodKod adalah seperti berikut:
Kod adalah seperti berikut:
Hasil pelaksanaan kod di atas sentiasa 2, 1, kerana hanya selepas baris kedua dilaksanakan, sistem akan melaksanakan fungsi panggil balik dalam "gilir tugas".
Standard HTML5 menetapkan bahawa nilai minimum (selang terpendek) parameter kedua setTimeout() mestilah tidak kurang daripada 4 milisaat Jika lebih rendah daripada nilai ini, ia akan meningkat secara automatik. Sebelum ini, pelayar lama menetapkan selang minimum kepada 10 milisaat.
Selain itu, perubahan DOM tersebut (terutamanya yang melibatkan pemaparan semula halaman) biasanya tidak dilaksanakan serta-merta, tetapi setiap 16 milisaat. Pada masa ini, kesan penggunaan requestAnimationFrame() adalah lebih baik daripada setTimeout().
Perlu diingat bahawa setTimeout() hanya memasukkan acara ke dalam "baris gilir tugas". Jika kod semasa mengambil masa yang lama, ia mungkin mengambil masa yang lama, jadi tiada cara untuk menjamin bahawa fungsi panggil balik akan dilaksanakan pada masa yang ditentukan oleh setTimeout().
6. Gelung Peristiwa Node.js
Node.js juga merupakan Gelung Acara berbenang tunggal, tetapi mekanisme pengendaliannya berbeza daripada persekitaran penyemak imbas.
Sila lihat rajah di bawah (oleh @BusyRich).
Mengikut gambar di atas, mekanisme pengendalian Node.js adalah seperti berikut.
(1) Enjin V8 menghuraikan skrip JavaScript.
(2) Kod yang dihuraikan memanggil API Node.
(3) Pustaka libuv bertanggungjawab untuk pelaksanaan API Node. Ia memperuntukkan tugas yang berbeza kepada utas yang berbeza untuk membentuk Gelung Acara (gelung peristiwa), dan mengembalikan hasil pelaksanaan tugas kepada enjin V8 secara tak segerak.
(4) Enjin V8 mengembalikan hasilnya kepada pengguna.
Selain daripada dua kaedah setTimeout dan setInterval, Node.js juga menyediakan dua kaedah lain yang berkaitan dengan "gilir tugas": process.nextTick dan setImmediate. Mereka boleh membantu kita memperdalam pemahaman kita tentang "barisan tugas".
Kaedah process.nextTick boleh mencetuskan fungsi panggil balik pada penghujung "timbunan pelaksanaan" semasa sebelum utas utama membaca "gilir tugas" kali seterusnya. Iaitu, tugas yang ditentukannya sentiasa berlaku sebelum semua tugas tak segerak. Kaedah setImmediate mencetuskan fungsi panggil balik pada penghujung "baris gilir tugas" semasa, iaitu, tugas yang ditentukannya sentiasa dilaksanakan pada kali seterusnya benang utama membaca "baris gilir tugas", yang hampir sama dengan setTimeout( fn, 0). Lihat contoh di bawah (melalui StackOverflow).
setTimeout(function timeout() {
console.log('TIMEOUT FIRED');
}, 0)
// 1
// 2
// TIMEOUT FIRED
Sekarang, lihat setImmediate.
setTimeout(function timeout() {
console.log('TIMEOUT FIRED');
}, 0)
// 1
// TIMEOUT FIRED
// 2
Dalam kod di atas, terdapat dua setImmediate. SetImmediate pertama menentukan bahawa fungsi panggil balik A dicetuskan pada penghujung "gilir tugas" semasa ("gelung peristiwa" seterusnya, setTimeout juga menentukan bahawa tamat masa fungsi panggil balik dicetuskan pada penghujung "tugasan semasa); baris gilir", jadi dalam hasil output, TIMEOUT FIRED berada di belakang 1. Bagi 2 kedudukan di belakang TIMEOUT FIRED, ia adalah kerana satu lagi ciri penting setImmediate: "gelung peristiwa" hanya boleh mencetuskan satu fungsi panggil balik yang ditentukan oleh setImmediate.
Kami mendapat perbezaan penting daripada ini: penyataan berbilang process.nextTick sentiasa dilaksanakan pada satu masa, manakala berbilang setPernyataan segera perlu dilaksanakan berbilang kali. Sebenarnya, inilah sebab mengapa Node.js versi 10.0 menambah kaedah setImmediate, jika tidak, panggilan rekursif ke process.nextTick seperti berikut akan menjadi tidak berkesudahan dan utas utama tidak akan membaca "baris gilir acara" sama sekali!