Artikel ini membawakan anda pengetahuan yang berkaitan tentang javascript terutamanya isu berkaitan tentang mekanisme dan prinsip pengendalian JavaScript, termasuk enjin penghuraian dan kandungan lain Mari kita lihat bersama-sama .
[Cadangan berkaitan: tutorial video javascript, bahagian hadapan web]
Saya telah menulis js selama lebih daripada dua tahun , saya tidak pernah memahami mekanisme dan prinsip operasinya dengan baik Hari ini saya merekodkan teori-teori sarjana dan ringkasan saya sendiri di bawah:
Yang di sebelah kanan mewakili perbezaan antara 1 dan 1 Lakukan penambahan
Tanda sama di tengah menunjukkan bahawa ini adalah pernyataan tugasan
Titik koma di hujung menunjukkan penghujung pernyataan ini
Ini adalah peraturan dengannya ukuran, dan enjin JavaScript akan kod JavaScript boleh dihuraikan mengikut piawaian ini. Kemudian ECMAScript di sini mentakrifkan peraturan ini. Antaranya, dokumen ECMAScript 262 mentakrifkan satu set lengkap standard untuk bahasa JavaScript. Ini termasuk:
abstrak, int, panjang, dsb. ialah perkataan simpanan JavaScript
Cara mengira sebagai nombor dan cara untuk dikira sebagai rentetan dsb.
mentakrifkan operator ( , -, >, mentakrifkan sintaks JavaScript
mentakrifkan algoritma pemprosesan standard untuk ungkapan, pernyataan, dsb., seperti semasa menghadapi == Cara menangani
⋯⋯
Enjin JavaScript standard akan dilaksanakan mengikut set dokumen ini Ambil perhatian bahawa standard ditekankan di sini, kerana terdapat juga pelaksanaan yang tidak mengikut piawaian, seperti Enjin JS IE. Inilah sebabnya JavaScript mempunyai masalah keserasian. Mengenai mengapa enjin JS IE tidak dilaksanakan mengikut piawaian, ia datang kepada perang pelayar saya tidak akan pergi ke butiran di sini.
Jadi, untuk mengelakkan kerumitan, JavaScript telah menjadi satu utas sejak ia dilahirkan Ini telah menjadi ciri teras bahasa ini dan tidak akan berubah pada masa hadapan.
Untuk memanfaatkan kuasa pengkomputeran CPU berbilang teras, HTML5 mencadangkan piawaian Pekerja Web, yang membenarkan skrip JavaScript mencipta berbilang rangkaian, tetapi urutan anak dikawal sepenuhnya oleh utas utama dan mesti tidak mengendalikan DOM. Oleh itu, piawaian baharu ini tidak mengubah sifat berbenang tunggal JavaScript.
Proses ialah unit terkecil peruntukan sumber CPU dan proses boleh mengandungi berbilang utas. Penyemak imbas adalah berbilang proses, dan setiap tetingkap penyemak imbas yang dibuka adalah satu proses.
Thread ialah unit terkecil penjadualan CPU Ruang memori program dikongsi antara utas dalam proses yang sama.
Proses ini boleh dianggap sebagai gudang, dan benang itu adalah trak yang boleh diangkut pada masa yang sama, tetapi setiap kenderaan hanya boleh melakukan satu perkara sahaja iaitu mengangkut kargo ini.
Inti teras:
Proses ialah unit terkecil peruntukan sumber CPU (unit terkecil yang boleh memiliki sumber dan berjalan secara bebas)
Thread ialah CPU Unit penjadualan terkecil (benang ialah unit yang menjalankan program berdasarkan proses. Terdapat berbilang benang dalam proses)
Proses yang berbeza juga boleh berkomunikasi, tetapi pada kos yang lebih tinggi.
Penyemak imbas adalah pelbagai proses
Selepas memahami perbezaan antara proses dan urutan, mari kita fahami tertentu tentang penyemak imbas: (Mari kita lihat pemahaman yang dipermudahkan dahulu)
proses boleh dijalankan adalah kerana sistem memperuntukkan sumber (cpu, memori) kepada prosesnya
Untuk memahami secara ringkas, setiap kali anda membuka halaman Tab, ia adalah bersamaan dengan mencipta Proses penyemak imbas yang berasingan.
Ambil Chrome sebagai contoh, ia mempunyai berbilang halaman tab dan anda boleh melihat berbilang proses dalam pengurus tugas Chrome (setiap halaman Tab mempunyai proses bebas dan proses utama ), yang juga boleh dilihat dalam Pengurus Tugas Windows.
Nota: Penyemak imbas juga harus mempunyai mekanisme pengoptimumannya sendiri di sini Kadangkala selepas membuka berbilang halaman tab, anda boleh melihat dalam Pengurus Tugas Chrome bahawa beberapa proses telah digabungkan (jadi setiap label Tab sepadan dengan proses A. tidak semestinya mutlak)
Setelah mengetahui bahawa penyemak imbas adalah pelbagai proses, mari kita lihat proses yang terkandung di dalamnya: (Untuk memudahkan pemahaman, Hanya proses utama disenaraikan)
(1) Proses penyemak imbas: Terdapat hanya satu proses utama penyemak imbas (bertanggungjawab untuk penyelarasan dan kawalan). Fungsi:
(2) Proses pemalam pihak ketiga: Setiap jenis pemalam sepadan dengan proses, yang hanya dibuat apabila pemalam digunakan
(3) Proses GPU: sehingga Satu, digunakan untuk lukisan 3D, dsb.
(4) Proses pemaparan penyemak imbas (inti penyemak imbas, proses pemapar, berbilang benang dalaman): Secara lalai, setiap halaman Tab mempunyai satu proses dan tidak menjejaskan satu sama lain. Fungsi utama ialah: pemaparan halaman, pelaksanaan skrip, pemprosesan acara, dsb.
Tingkatkan ingatan: membuka halaman web dalam penyemak imbas adalah bersamaan dengan memulakan proses baharu (proses itu mempunyai berbilang benangnya sendiri)
Sudah tentu, penyemak imbas kadangkala menggabungkan berbilang proses (contohnya, selepas membuka berbilang tab kosong, anda akan mendapati berbilang tab kosong digabungkan menjadi satu proses)
Berbanding dengan penyemak imbas satu proses, pelbagai proses mempunyai kelebihan berikut:
Pemahaman yang mudah:
Jika penyemak imbas adalah satu proses, maka jika halaman tab tertentu terhempas, ia akan menjejaskan seluruh penyemak imbas, dan pengalaman itu akan menjadi miskin; adalah satu proses, jika pemalam ranap, ia juga akan menjejaskan keseluruhan penyemak imbas.
Sudah tentu, memori dan penggunaan sumber lain juga akan lebih besar, yang bermaksud ruang ditukar dengan masa. Tidak kira berapa besar memori itu, ia tidak mencukupi untuk Chrome Masalah kebocoran memori telah diperbaiki sedikit sekarang. Ia hanya peningkatan, dan ia juga akan membawa kepada peningkatan penggunaan kuasa.
Inilah perkara utama yang kita dapat lihat bahawa terdapat begitu banyak proses yang disebutkan di atas, jadi, untuk operasi bahagian hadapan biasa, yang paling diperlukan ialah apa? Jawapannya ialah proses rendering.
Boleh difahami bahawa pemaparan halaman, pelaksanaan JS dan gelung acara semuanya dilakukan dalam proses ini. Seterusnya, fokus pada menganalisis proses ini
Sila ingat bahawa proses pemaparan penyemak imbas adalah berbilang benang (enjin JS adalah satu benang)
Kemudian mari kita ambil lihatlah benang manakah yang disertakan (senarai beberapa utas pemastautin utama):
Melihat perkara ini, pertama sekali, anda harus mempunyai pemahaman tertentu tentang proses dan urutan dalam pelayar, kemudian Seterusnya, mari kita bercakap tentang bagaimana proses Penyemak imbas (proses kawalan) berkomunikasi dengan kernel Selepas memahami perkara ini, kita boleh menyambung bahagian pengetahuan ini bersama-sama untuk mempunyai konsep yang lengkap dari awal hingga akhir.
Jika anda membuka pengurus tugas dan kemudian membuka penyemak imbas, anda boleh melihat: dua proses muncul dalam pengurus tugas (satu ialah proses kawalan utama dan satu lagi proses pemaparan yang membuka halaman Tab) , dan kemudian di bawah premis ini, mari kita lihat keseluruhan proses: (banyak dipermudahkan)
Berikut ialah gambar lukisan mudah: (sangat dipermudahkan)
Pada ketika ini, anda telah pun mengetahui tentang pengendalian penyemak imbas Mari kita fahami konsep keseluruhan Seterusnya, mari kita selesaikan secara ringkas beberapa konsep
Memandangkan JavaScript boleh memanipulasi DOM, jika anda mengubah suai atribut elemen ini semasa memaparkan antara muka (iaitu, utas JS dan UI thread dijalankan pada masa yang sama), maka thread rendering sebelum dan selepas Data elemen yang diperoleh mungkin tidak konsisten.
Oleh itu, untuk mengelakkan hasil pemaparan yang tidak dijangka, penyemak imbas menetapkan urutan pemaparan GUI dan enjin JS menjadi saling eksklusif Apabila enjin JS dilaksanakan, utas GUI akan digantung dan kemas kini GUI akan disimpan dalam Tunggu dalam baris gilir untuk dilaksanakan dengan segera apabila benang enjin JS melahu.
Daripada perhubungan yang saling eksklusif di atas, dapat disimpulkan bahawa JS akan menyekat halaman jika ia mengambil masa terlalu lama untuk dilaksanakan.
Sebagai contoh, dengan mengandaikan bahawa enjin JS melakukan sejumlah besar pengiraan, walaupun GUI dikemas kini pada masa ini, ia akan disimpan dalam baris gilir dan menunggu pelaksanaan apabila enjin JS melahu. Kemudian, disebabkan jumlah pengiraan yang banyak, enjin JS mungkin akan melahu untuk masa yang lama dan lama, dan secara semula jadi ia akan berasa sangat besar.
Oleh itu, cuba elakkan pelaksanaan JS mengambil masa yang terlalu lama, yang akan menyebabkan pemaparan halaman menjadi tidak konsisten, yang membawa kepada perasaan bahawa pemaparan dan pemuatan halaman disekat.
Untuk menyelesaikan masalah ini, selain meletakkan pengiraan pada bahagian belakang, jika ia tidak dapat dielakkan dan pengiraan besar berkaitan dengan UI, maka idea saya ialah menggunakan setTimeout untuk membahagikan tugas dan memberikan sedikit masa lapang di tengah Masa untuk membiarkan enjin JS mengendalikan UI supaya halaman tidak membeku secara langsung.
Jika anda membuat keputusan terus tentang versi HTML5 minimum yang diperlukan, anda boleh melihat WebWorker di bawah.
Seperti yang dinyatakan dalam artikel sebelumnya, enjin JS adalah single-threading, dan jika masa pelaksanaan JS terlalu lama, ia akan menyekat halaman, jadi JS akan benar-benar Adakah ia tidak berkuasa untuk pengiraan intensif CPU?
Jadi, kemudian, Pekerja Web disokong dalam HTML5.
Penjelasan rasmi MDN ialah:
Web Worker menyediakan cara mudah untuk menjalankan skrip dalam urutan latar belakang untuk kandungan web.
Thread boleh melaksanakan tugas tanpa mengganggu antara muka pengguna Pekerja ialah objek yang dibuat menggunakan pembina (Worker()) yang menjalankan fail JavaScript bernama (fail ini mengandungi kod yang akan dijalankan dalam pekerja. benang).
Pekerja berjalan dalam konteks global yang lain, berbeza daripada tetingkap semasa.
Oleh itu, menggunakan pintasan tetingkap untuk mendapatkan skop global semasa (bukan diri sendiri) dalam Pekerja akan mengembalikan ralat
Fahami dengan cara ini:
Apabila mencipta Pekerja, JS Enjin digunakan pada penyemak imbas untuk membuka sub-benang (sub-benang dibuka oleh penyemak imbas, dikawal sepenuhnya oleh utas utama dan tidak boleh mengendalikan DOM)
Benang enjin JS dan pekerja thread berkomunikasi melalui kaedah tertentu (postMessage API, yang perlu dihantar Serialize objek untuk berinteraksi dengan thread untuk data tertentu)
Oleh itu, jika terdapat kerja yang sangat memakan masa, sila buka thread Worker yang berasingan, supaya tidak kira betapa gegarannya bumi, ia tidak akan menjejaskan benang utama enjin JS, dan hanya hasilnya akan dikira Selepas itu, hanya sampaikan hasilnya kepada utas utama, sempurna!
Dan sila ambil perhatian bahawa. Enjin JS adalah satu benang Intipati ini tidak berubah.
Yang lain, penjelasan terperinci Pekerja adalah di luar skop artikel ini, jadi saya tidak akan menerangkan secara terperinci.
Sekarang kita berada di sini, mari sebut SharedWorker sekali lagi (untuk mengelakkan kekeliruan kedua-dua konsep ini nanti)
WebWorker hanya Kepunyaan halaman tertentu dan tidak akan dikongsi dengan proses Render (proses kernel penyemak imbas) halaman lain
Jadi Chrome mencipta urutan baharu dalam proses Render (setiap halaman Tab ialah proses Render) untuk menjalankan JavaScript dalam program Worker .
SharedWorker dikongsi oleh semua halaman penyemak imbas dan tidak boleh dilaksanakan dengan cara yang sama seperti Worker kerana ia tidak bergabung dengan proses Render dan boleh dikongsi oleh berbilang proses Render
Oleh itu, penyemak imbas Chrome mencipta pelayar berasingan SharedWorker Proses digunakan untuk menjalankan program JavaScript Hanya terdapat satu proses SharedWorker untuk setiap JavaScript yang sama dalam penyemak imbas, tidak kira berapa kali ia dicipta.
Selepas melihat ini, ia sepatutnya mudah difahami, pada asasnya, ia adalah perbezaan antara proses dan utas. SharedWorker diuruskan oleh proses bebas dan WebWorker hanyalah rangkaian di bawah proses Render.
Tambahkan proses pemaparan penyemak imbas (versi ringkas)
Untuk memudahkan pemahaman, kerja awal secara langsung ditinggalkan sebagai:
Semak Imbas Penyemak imbas memasukkan URL, proses utama penyemak imbas mengambil alih, membuka utas muat turun, dan kemudian membuat permintaan http (meninggalkan pertanyaan DNS, pengalamatan IP, dll.), kemudian menunggu respons, mendapatkan kandungan, dan kemudian memindahkan kandungan ke proses Renderer melalui antara muka RendererHost
Proses pemaparan penyemak imbas bermula
Selepas kernel pelayar mendapat kandungan, pemaparan boleh dibahagikan secara kasar kepada langkah berikut:
Menghuraikan HTML dan membina pepohon DOM
Menghuraikan CSS dan membina pepohon pemaparan (menghuraikan kod CSS ke dalam struktur data berbentuk pepohon, dan kemudian menggabungkannya dengan DOM untuk bergabung menjadi pepohon pemaparan)
Pepohon pemaparan reka letak (Layout/ reflow), bertanggungjawab untuk setiap Pengiraan saiz dan kedudukan elemen
Lukiskan pokok pemaparan (cat), lukis maklumat piksel halaman
Penyemak imbas akan menghantar maklumat setiap lapisan kepada GPU, dan GPU akan menggabungkan setiap lapisan dan memaparkannya pada skrin.
Semua langkah terperinci telah diabaikan Selepas pemaparan, ia adalah acara pemuatan, dan kemudian ia adalah pemprosesan logik JS anda sendiri.
Memandangkan beberapa langkah terperinci telah ditinggalkan, izinkan saya menyebut beberapa butiran yang mungkin memerlukan perhatian.
Seperti yang dinyatakan di atas, acara pemuatan akan dicetuskan selepas pemaparan selesai, jadi anda boleh membezakan urutan acara pemuatan dan acara DOMContentLoaded ?
Ia sangat mudah, hanya tahu takrifnya:
Apabila acara DOMContentLoaded dicetuskan, hanya apabila DOM dimuatkan, tidak termasuk helaian gaya, imej, skrip async, dsb.
Apabila acara onload dicetuskan, semua DOM, helaian gaya, skrip dan imej pada halaman telah dimuatkan, iaitu, dipaparkan
Oleh itu, susunannya ialah: DOMContentLoaded -> >Sama ada pemuatan css akan menyekat pemaparan pokok DOM
Pertama sekali, kita semua tahu bahawa css dimuat turun secara tidak segerak oleh yang berasingan muat turun benang.
Kemudian mari kita bincangkan tentang fenomena berikut:
Pemuatan CSS tidak akan menyekat penghuraian pokok DOM (DOM dibina seperti biasa semasa pemuatan tak segerak)Jadi hanya menghuraikan struktur pokok DOM dahulu, selesaikan kerja yang boleh dilakukan, dan kemudian tunggu css anda dimuatkan, dan kemudian render pokok render mengikut gaya akhir prestasi, pendekatan ini Ia sememangnya akan menjadi lebih baik.
Lapisan biasa dan lapisan komposit
Ia boleh difahami secara ringkas seperti ini Lapisan yang diberikan oleh penyemak imbas biasanya merangkumi dua kategori: lapisan biasa dan lapisan komposit
Pertama sekali, aliran dokumen biasa boleh difahami sebagai a lapisan komposit. (Ini dipanggil lapisan komposit lalai. Tidak kira berapa banyak elemen yang ditambahkan padanya, mereka sebenarnya berada dalam lapisan komposit yang sama)
Kedua, susun atur mutlak (yang sama berlaku untuk tetap) , walaupun ia boleh dipisahkan daripada aliran dokumen biasa, ia Masih tergolong dalam lapisan komposit lalai.
Kemudian, anda boleh mengisytiharkan lapisan komposit baharu melalui pecutan perkakasan, yang akan memperuntukkan sumber secara berasingan (sudah tentu ia juga akan dipisahkan daripada aliran dokumen biasa, supaya tidak kira bagaimana lapisan komposit berubah, Ia akan tidak menjejaskan lukis semula aliran semula dalam lapisan komposit lalai)
Ia boleh difahami dengan mudah: dalam GPU, setiap lapisan komposit dilukis secara berasingan, jadi ia tidak menjejaskan satu sama lain, itulah sebabnya beberapa kesan pecutan perkakasan adegan Kelas pertama
Anda boleh melihatnya dalam Chrome DevTools --> Lagi Alat --> ia menjadi Lapisan komposit (pecutan perkakasan)
Tukar elemen ini menjadi lapisan komposit, iaitu teknologi pecutan perkakasan legenda
atribut will-chang (ini lebih jauh), biasanya digunakan bersempena dengan kelegapan dan menterjemah, fungsinya Ia adalah untuk memberitahu penyemak imbas terlebih dahulu bahawa perubahan perlu dibuat, supaya penyemak imbas akan mula melakukan beberapa kerja pengoptimuman (sebaik-baiknya melepaskan ini selepas digunakan)
video, iframe, kanvas, webgl dan elemen lain yang lain, seperti pemalam denyar sebelumnya
Perbezaan antara pecutan mutlak dan perkakasan
Ia dapat dilihat bahawa walaupun mutlak boleh dipisahkan daripada yang biasa aliran dokumen, ia tidak boleh dipisahkan daripada lapisan komposit lalai. Oleh itu, walaupun maklumat dalam perubahan mutlak, ia tidak akan mengubah pepohon pemaparan dalam aliran dokumen biasa Walau bagaimanapun, apabila penyemak imbas akhirnya melukis, keseluruhan lapisan komposit dilukis, jadi perubahan dalam maklumat secara mutlak masih akan menjejaskan lukisan. daripada keseluruhan lapisan komposit. (Pelayar akan melukis semulanya. Jika terdapat banyak kandungan dalam lapisan komposit, maklumat lukisan yang dibawa oleh mutlak akan berubah terlalu banyak, dan penggunaan sumber akan menjadi sangat serius)
Dan pecutan perkakasan adalah secara langsung dalam lapisan komposit lain ( Mula dari awal), jadi perubahan maklumatnya tidak akan menjejaskan lapisan komposit lalai (sudah tentu, lapisan komposit dalaman pasti akan menjejaskan lapisan kompositnya sendiri), ia hanya mencetuskan komposisi akhir (pandangan keluaran)
Secara amnya, selepas pecutan perkakasan dihidupkan, elemen akan menjadi lapisan komposit, yang boleh bebas daripada aliran dokumen biasa Selepas pengubahsuaian, ia boleh mengelakkan lukisan semula keseluruhan halaman dan tingkatkan prestasi, tetapi cuba Jangan gunakan sebilangan besar lapisan komposit, jika tidak disebabkan penggunaan sumber yang berlebihan, halaman akan menjadi lebih tersekat
Apabila menggunakan pecutan perkakasan, gunakan indeks sebanyak mungkin untuk menghalang Penyemak imbas mencipta pemaparan lapisan komposit untuk elemen berikutnya secara lalai
Prinsip khusus adalah seperti berikut: Dalam webkit CSS3, jika elemen ini mempunyai pecutan perkakasan ditambah dan tahap indeks agak rendah, maka elemen lain di belakang elemen ini (Jika tahap lebih tinggi daripada elemen ini, atau sama, dan sifat releatif atau mutlak adalah sama), ia akan menjadi pemaparan lapisan komposit secara lalai tidak dikendalikan dengan betul, ia akan sangat mempengaruhi prestasi
Untuk memahaminya secara ringkas, ia sebenarnya boleh Ia dianggap sebagai konsep gubahan tersirat: jika a ialah lapisan komposit dan b berada di atas a, maka b juga akan secara tersirat ditukar menjadi lapisan komposit Ini memerlukan perhatian khusus.
Sehingga ke tahap ini, ia sudah pun selepas pemaparan awal halaman penyemak imbas, beberapa analisis mekanisme pengendalian daripada enjin JS.
Perhatikan bahawa kami tidak akan bercakap tentang konsep seperti konteks boleh laku, VO, rantai skop, dsb. (ini boleh disusun menjadi artikel lain di sini terutamanya tentang cara kod JS dilaksanakan bersama dengan Acara). gelung.
Prasyarat untuk membaca bahagian ini ialah anda sudah tahu bahawa enjin JS adalah satu benang, dan beberapa konsep yang dinyatakan di atas akan digunakan di sini:
JS dibahagikan kepada tugas segerak dan tugas tak segerak
Lihat gambar:
Melihat ini, anda seharusnya dapat memahami: Mengapakah peristiwa ditolak oleh setTimeout kadangkala tidak dilaksanakan tepat pada masanya? Oleh kerana utas utama mungkin belum melahu dan sedang melaksanakan kod lain apabila ia ditolak ke dalam senarai acara, jadi secara semula jadi akan ada ralat.
Gambar di atas secara kasar menerangkan:
Ambil perhatian bahawa anda sentiasa perlu menunggu kod dalam tindanan selesai melaksanakan sebelum membaca peristiwa dalam baris gilir acara
Inti mekanisme gelung acara di atas ialah: Benang enjin JS dan benang pencetus peristiwa
Tetapi ada adalah beberapa butiran tersembunyi dalam acara Contohnya, selepas memanggil setTimeout, Bagaimana untuk menunggu masa tertentu sebelum menambah baris gilir acara?
Adakah ia dikesan oleh enjin JS? Sudah tentu tidak. Ia dikawal oleh benang pemasa (kerana enjin JS itu sendiri terlalu sibuk dan tidak mempunyai masa untuk melakukan apa-apa lagi)
Mengapa kita memerlukan benang pemasa yang berasingan? Oleh kerana enjin JavaScript adalah satu-benang, jika ia berada dalam keadaan benang yang disekat, ia akan menjejaskan ketepatan pemasaan, jadi ia perlu membuka benang yang berasingan untuk pemasaan.
Bilakah benang pemasa akan digunakan? Apabila menggunakan setTimeout atau setInterval, ia memerlukan benang pemasa mengikut masa dan selepas masa selesai, acara tertentu akan ditolak ke dalam baris gilir acara.
Terdapat perbezaan antara menggunakan setTimeout untuk mensimulasikan pemasaan biasa dan menggunakan setInterval secara langsung.
Oleh kerana setiap kali setTimeout dilaksanakan, ia akan dilaksanakan, dan kemudian ia akan terus setTimeout selepas tempoh pelaksanaan akan terdapat ralat di tengah (ralat berkaitan dengan masa pelaksanaan kod)
Dan setInterval Ia adalah untuk menolak acara dengan tepat setiap kali Walau bagaimanapun, masa pelaksanaan sebenar acara mungkin juga tidak tepat sebelum acara selesai.
Dan setInterval mempunyai beberapa masalah maut:
Kesan kumulatif, jika kod setInterval belum menyelesaikan pelaksanaan sebelum ditambahkan pada baris gilir sekali lagi, ia akan menyebabkan kod pemasa berjalan beberapa kali berturut-turut tanpa selang waktu. Walaupun ia dilaksanakan pada selang waktu biasa, masa pelaksanaan berbilang kod setInterval mungkin lebih pendek daripada jangkaan (kerana pelaksanaan kod mengambil masa tertentu)
Contohnya, penyemak imbas seperti paparan web iOS atau Safari mempunyai ciri yang apabila menatal Jika anda tidak melaksanakan JS, jika anda menggunakan setInterval, anda akan mendapati bahawa ia akan dilaksanakan beberapa kali selepas penatalan Kerana penatalan tidak melaksanakan JS, panggilan balik terkumpul akan menyebabkan bekas menjadi beku dan menyebabkan beberapa ralat yang tidak diketahui. jika masa pelaksanaan panggil balik terlalu lama ( Bahagian ini akan ditambah kemudian. Pengoptimuman terbina dalam SetInterval tidak akan menambah panggilan balik berulang kali)
Dan apabila penyemak imbas diminimumkan dan dipaparkan, setInterval tidak akan melaksanakan program fungsi panggil balik setInterval dalam baris gilir , apabila tetingkap penyemak imbas dibuka semula, semua akan dilaksanakan dalam sekelip mata
Oleh itu, memandangkan begitu banyak masalah, penyelesaian terbaik pada masa ini biasanya dianggap sebagai: gunakan setTimeout untuk. simulasi setInterval, atau gunakan terus requestAnimationFrame
untuk majlis khas : Seperti yang dinyatakan dalam JS Elevation, enjin JS akan mengoptimumkan setInterval Jika terdapat panggilan balik setInterval dalam baris gilir acara semasa, ia tidak akan ditambah berulang kali.
Perkara di atas telah menyusun mekanisme gelung peristiwa JS, yang mencukupi dalam kes ES5 , tetapi kini ES6 berleluasa, anda masih akan menghadapi beberapa masalah, seperti soalan berikut:
console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); Promise.resolve().then(function() { console.log('promise1'); }).then(function() { console.log('promise2'); }); console.log('script end');
Hmm, susunan pelaksanaan yang betul adalah seperti ini:
script start script end promise1 promise2 setTimeout
Mengapa ? Kerana terdapat konsep baharu dalam Promise: microtask
Atau, selanjutnya, JS dibahagikan kepada dua jenis tugas: macrotask dan microtask Dalam ECMAScript, microtask dipanggil kerja, dan macrotask boleh dipanggil tugas.
Apakah definisi mereka? perbezaannya? Ringkasnya, ia boleh difahami seperti berikut:
Dapat difahami bahawa kod yang dilaksanakan oleh timbunan pelaksanaan setiap kali adalah makro. tugasan (termasuk setiap kali daripada acara Dapatkan panggilan balik acara daripada baris gilir dan masukkannya ke dalam tindanan pelaksanaan untuk pelaksanaan)
Mari kita fahami berdasarkan urutan:
(1) Acara dalam macrotask diletakkan dalam baris gilir acara, dan baris gilir ini dikekalkan oleh urutan pencetus acara
(2 ) Semua tugasan mikro dalam tugasan mikro ditambahkan pada baris gilir microtask (Barisan Kerja), menunggu pelaksanaan selepas tugasan makro semasa selesai, dan baris gilir ini dikekalkan oleh benang enjin JS (ini disimpulkan daripada pemahaman saya sendiri, kerana ia adalah dilaksanakan dengan lancar di bawah utas utama)
Jadi, untuk meringkaskan mekanisme pengendalian:
Selain itu, sila beri perhatian kepada perbezaan antara polyfill Promise dan versi rasmi:
Dalam versi rasmi, ia adalah bentuk microtask standard
Polyfill biasanya disimulasikan melalui setTimeout, jadi ia adalah dalam bentuk macrotask Ambil perhatian bahawa sesetengah penyemak imbas mempunyai pelaksanaan yang berbeza keputusan (kerana mereka mungkin melaksanakan tugas mikro sebagai tugasan makro), tetapi demi kesederhanaan, beberapa perbezaan tidak akan diterangkan di sini di bawah penyemak imbas standard (tetapi ingat, sesetengah penyemak imbas mungkin tidak standard)
补充:使用MutationObserver实现microtask
MutationObserver可以用来实现microtask (它属于microtask,优先级小于Promise, 一般是Promise不支持时才会这样做)
它是HTML5中的新特性,作用是:监听一个DOM变动, 当DOM对象树发生任何变动时,Mutation Observer会得到通知
像以前的Vue源码中就是利用它来模拟nextTick的, 具体原理是,创建一个TextNode并监听内容变化, 然后要nextTick的时候去改一下这个节点的文本内容, 如下:
var counter = 1 var observer = new MutationObserver(nextTickHandler) var textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) }
不过,现在的Vue(2.5+)的nextTick实现移除了MutationObserver的方式(据说是兼容性原因), 取而代之的是使用MessageChannel (当然,默认情况仍然是Promise,不支持才兼容的)。
MessageChannel属于宏任务,优先级是:MessageChannel->setTimeout, 所以Vue(2.5+)内部的nextTick与2.4及之前的实现是不一样的,需要注意下。
【相关推荐:javascript视频教程、web前端】
Atas ialah kandungan terperinci Kuasai sepenuhnya mekanisme operasi dan prinsip JavaScript. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!