Javascript bukan bahasa berbilang utas, tetapi bahasa satu utas. JavaScript ialah bahasa skrip penyemak imbas, dan penterjemahnya adalah single-threaded dan tujuan utama JavaScript adalah untuk berinteraksi dengan pengguna dan mengendalikan DOM, yang menentukan bahawa ia hanya boleh menjadi single-threaded, jika tidak, ia akan menyebabkan masalah penyegerakan yang sangat kompleks; .
Persekitaran pengendalian tutorial ini: sistem Windows 7, versi JavaScript 1.8.5, komputer Dell G3.
Javascript bukan bahasa berbilang utas, tetapi bahasa satu utas. Bahasa JavaScript juga tidak menyokong multi-threading kerana penterjemah JavaScript dalam penyemak imbas adalah single-threading.
Ciri utama bahasa JavaScript ialah ia adalah satu benang, yang bermaksud bahawa ia hanya boleh melakukan satu perkara pada satu masa.
Jadi, mengapa JavaScript tidak boleh mempunyai berbilang urutan? Ini boleh meningkatkan kecekapan.
Benang tunggal JavaScript adalah 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.
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.
Baris Gilir Tugasan
Urutan tunggal bermakna semua tugasan perlu dibariskan, dan tugasan sebelumnya akan diselesaikan sebelum tugasan seterusnya dilaksanakan. Jika tugasan sebelumnya mengambil masa yang lama, tugasan seterusnya perlu menunggu.
Jika giliran 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 (contohnya , operasi Ajax dibaca dari rangkaian (dapatkan data), anda perlu menunggu keputusan keluar sebelum meneruskan.
Pereka bentuk bahasa JavaScript menyedari bahawa pada masa ini, utas utama 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.
Jadi, semua tugas boleh dibahagikan kepada dua jenis, satu tugas segerak (synchronous) dan satu lagi tugas tak segerak (asynchronous).
Tugas segerak merujuk kepada tugasan yang dibariskan untuk pelaksanaan pada urutan utama. Tugasan seterusnya hanya boleh dilaksanakan selepas tugasan sebelumnya telah dilaksanakan; utas utama . Untuk tugasan yang memasuki "baris gilir tugas", hanya apabila "baris gilir tugas" memberitahu utas utama bahawa tugas tak segerak boleh dilaksanakan, tugasan itu akan memasuki utas utama untuk dilaksanakan.
Secara khusus, mekanisme pengendalian pelaksanaan tak segerak adalah seperti berikut. (Hal yang sama berlaku untuk pelaksanaan segerak, kerana ia boleh dianggap sebagai pelaksanaan tak segerak tanpa tugas tak segerak.)
(1) Semua tugas segerak dilaksanakan pada urutan utama, membentuk tindanan konteks pelaksanaan.
(2) Sebagai tambahan kepada utas utama, terdapat juga "gilir tugas". Selagi tugas asynchronous mempunyai hasil yang sedang berjalan, acara diletakkan dalam "baris gilir tugas".
(3) Setelah semua tugasan penyegerakan dalam "tindanan pelaksanaan" selesai, sistem akan membaca "gilir tugas" untuk melihat peristiwa yang ada di dalamnya. Tugas tak segerak yang sepadan itu menamatkan keadaan menunggu, masukkan timbunan pelaksanaan dan mulakan 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 tugasan Ini ialah mekanisme berjalan JavaScript". Proses ini terus berulang.
Acara dan fungsi panggil balik"Baris gilir tugas" ialah baris gilir acara (ia juga boleh difahami sebagai baris gilir mesej apabila peranti IO selesai a tugasan, ia Menambah acara pada "baris gilir tugas" menunjukkan bahawa tugas tak segerak yang berkaitan boleh memasuki "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" (panggilan balik) ialah kod yang akan digantung oleh urutan utama. Tugas tak segerak mesti menentukan fungsi panggil balik Apabila utas utama mula melaksanakan tugas tak segerak, fungsi panggil balik yang sepadan dilaksanakan.
"Baris gilir tugas" ialah struktur data masuk dahulu, keluar dahulu Acara yang berada di kedudukan pertama dibaca dahulu oleh urutan utama. Proses membaca utas utama pada asasnya adalah automatik Sebaik sahaja timbunan pelaksanaan dikosongkan, acara pertama pada "gilir tugas" akan memasuki utas utama secara automatik. Walau bagaimanapun, disebabkan oleh fungsi "pemasa" yang disebut kemudian, utas utama mesti terlebih dahulu menyemak masa pelaksanaan tertentu hanya boleh kembali ke utas utama selepas masa yang ditentukan.
Gelung AcaraUrut utama membaca peristiwa daripada "baris gilir tugasan". Proses ini adalah kitaran, jadi keseluruhan mekanisme operasi juga dipanggil Gelung Peristiwa. Untuk lebih memahami Gelung Acara, sila lihat gambar di bawah. Dalam gambar di atas, apabila utas utama berjalan, timbunan dan timbunan dijana Kod dalam timbunan memanggil pelbagai API luaran. tugas" Tambah pelbagai acara (klik, muatkan, selesai) pada baris gilir". 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 (tugas segerak) sentiasa dilaksanakan sebelum membaca "gilir tugas" (tugas tak segerak). Lihat contoh di bawah. Kaedah req.send dalam kod di atas ialah operasi Ajax untuk menghantar data ke pelayan Ia adalah tugas tak segerak, yang bermaksud bahawa sistem tidak akan membaca tugas sehingga semua kod skrip semasa dilaksanakan. Oleh itu, ia bersamaan dengan tulisan berikut. Maksudnya, bahagian yang menentukan fungsi panggil balik (onload dan onerror) tidak kira sebelum atau selepas kaedah send(), kerana ia tergolong dalam sebahagian daripada timbunan pelaksanaan, dan sistem sentiasa melaksanakannya, akan membaca "baris gilir tugas". Pemasa Selain meletakkan acara untuk tugas tak segerak, "baris gilir tugas" juga boleh meletakkan acara bermasa, iaitu, menentukan tempoh kod tertentu akan dilaksanakan selepas. Ini dipanggil fungsi "pemasa", iaitu kod yang dilaksanakan dengan kerap. Fungsi pemasa terutamanya dilengkapkan oleh dua fungsi setTimeout() dan setInterval() Mekanisme operasi dalaman mereka adalah sama persis kod yang ditentukan oleh bekas adalah dilaksanakan sekali , yang terakhir dilaksanakan berulang kali. Yang berikut terutamanya membincangkan setTimeout(). setTimeout() menerima dua parameter, yang pertama ialah fungsi panggil balik dan yang kedua ialah bilangan milisaat untuk menangguhkan pelaksanaan. Hasil pelaksanaan kod di atas ialah 1, 3, 2, kerana setTimeout() menangguhkan pelaksanaan baris kedua sehingga 1000 milisaat kemudian. Jika parameter kedua setTimeout() ditetapkan kepada 0, ini bermakna selepas kod semasa dilaksanakan (timbunan pelaksanaan dikosongkan), fungsi panggil balik yang ditentukan akan dilaksanakan serta-merta (selang 0 milisaat) Hasil pelaksanaan kod di atas sentiasa 2, 1, kerana hanya selepas baris kedua dilaksanakan, sistem akan melaksanakan fungsi panggil balik dalam "baris gilir tugas". Ringkasnya, maksud setTimeout(fn,0) ialah untuk menentukan tugasan yang akan dilaksanakan dalam masa terbiar terawal yang tersedia bagi utas utama, iaitu, untuk dilaksanakan seawal mungkin. Ia menambahkan acara pada penghujung "baris gilir tugas", jadi ia tidak akan dilaksanakan sehingga tugas penyegerakan dan acara sedia ada dalam "baris gilir tugas" telah diproses. Piawaian 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 diambil perhatian bahawa setTimeout() hanya memasukkan acara ke dalam "baris gilir tugasan Utas utama mesti menunggu sehingga kod semasa (timbunan pelaksanaan) selesai dilaksanakan sebelum utas utama melaksanakan fungsi panggil balik itu". menentukan. 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(). Gelung Peristiwa Node.js Node.js juga merupakan Gelung Peristiwa satu benang, tetapi mekanisme pengendaliannya berbeza daripada persekitaran penyemak imbas. Sila lihat rajah di bawah Mengikut rajah di atas, mekanisme pengendalian Node.js adalah seperti berikut. (1) Enjin V8 menghuraikan skrip JavaScript. (2) Kod yang dihuraikan memanggil API Nod. (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 "task queue": process.nextTick dan setImmediate. Mereka boleh membantu kita memperdalam pemahaman kita tentang "barisan tugas". Kaedah process.nextTick boleh mencetuskan fungsi panggil balik pada penghujung "tindanan pelaksanaan" semasa—sebelum Gelung Peristiwa seterusnya (urutan utama membaca "barisan tugas"). Iaitu, tugas yang ditentukannya sentiasa berlaku sebelum semua tugas tak segerak. Kaedah setImmediate menambah acara ke penghujung "baris gilir tugas" semasa, iaitu, tugas yang ditentukannya sentiasa dilaksanakan dalam Gelung Acara seterusnya, yang hampir sama dengan setTimeout(fn, 0). Lihat contoh di bawah (melalui StackOverflow). 上面代码中,由于process.nextTick方法指定的回调函数,总是在当前”执行栈”的尾部触发,所以不仅函数A比setTimeout指定的回调函数timeout先执行,而且函数B也比timeout先执行。这说明,如果有多个process.nextTick语句(不管它们是否嵌套),将全部在当前”执行栈”执行。 现在,再看setImmediate。 上面代码中,setImmediate与setTimeout(fn,0)各自添加了一个回调函数A和timeout,都是在下一次Event Loop触发。那么,哪个回调函数先执行呢?答案是不确定。运行结果可能是1–TIMEOUT FIRED–2,也可能是TIMEOUT FIRED–1–2。 令人困惑的是,Node.js文档中称,setImmediate指定的回调函数,总是排在setTimeout前面。实际上,这种情况只发生在递归调用的时候。 上面代码中,setImmediate和setTimeout被封装在一个setImmediate里面,它的运行结果总是1–TIMEOUT FIRED–2,这时函数A一定在timeout前面触发。至于2排在TIMEOUT FIRED的后面(即函数B在timeout后面触发),是因为setImmediate总是将事件注册到下一轮Event Loop,所以函数A和timeout是在同一轮Loop执行,而函数B在下一轮Loop执行。 我们由此得到了process.nextTick和setImmediate的一个重要区别:多个process.nextTick语句总是在当前”执行栈”一次执行完,多个setImmediate可能则需要多次loop才能执行完。事实上,这正是Node.js 10.0版添加setImmediate方法的原因,否则像下面这样的递归调用process.nextTick,将会没完没了,主线程根本不会去读取”事件队列”! 事实上,现在要是你写出递归的process.nextTick,Node.js会抛出一个警告,要求你改成setImmediate。 另外,由于process.nextTick指定的回调函数是在本次”事件循环”触发,而setImmediate指定的是在下次”事件循环”触发,所以很显然,前者总是比后者发生得早,而且执行效率也高(因为不用检查”任务队列”)。 【相关推荐:javascript学习教程】 var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function (){};
req.onerror = function (){};
req.send();
var req = new XMLHttpRequest();
req.open('GET', url);
req.send();
req.onload = function (){};
req.onerror = function (){};
console.log(1);
setTimeout(function(){console.log(2);},1000);
console.log(3);
setTimeout(function(){console.log(1);}, 0);
console.log(2);
process.nextTick(function A() {
console.log(1);
process.nextTick(function B(){console.log(2);});
});
setTimeout(function timeout() {
console.log('TIMEOUT FIRED');
}, 0)
// 1
// 2
// TIMEOUT FIRED
setImmediate(function A() {
console.log(1);
setImmediate(function B(){console.log(2);});
});
setTimeout(function timeout() {
console.log('TIMEOUT FIRED');
}, 0);
setImmediate(function (){
setImmediate(function A() {
console.log(1);
setImmediate(function B(){console.log(2);});
});
setTimeout(function timeout() {
console.log('TIMEOUT FIRED');
}, 0);
});
// 1
// TIMEOUT FIRED
// 2
process.nextTick(function foo() {
process.nextTick(foo);
});
Atas ialah kandungan terperinci Adakah javascript bahasa berbilang benang?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!