Node "Loop Event" adalah inti dari kemampuannya untuk menangani konkurensi besar dan throughput tinggi. Ini adalah tempat yang paling ajaib. Menurut ini, Node.js pada dasarnya dapat dipahami sebagai "satu threading", dan juga memungkinkan operasi sewenang-wenang untuk diproses di latar belakang. Artikel ini akan menggambarkan cara kerja loop acara dan Anda dapat merasakan keajaibannya juga.
Pemrograman yang digerakkan oleh acara
Untuk memahami loop acara, pertama -tama kita harus memahami pemrograman drive acara. Itu muncul pada tahun 1960. Hari ini, pemrograman yang digerakkan oleh acara banyak digunakan dalam pemrograman UI. Salah satu penggunaan utama JavaScript adalah untuk berinteraksi dengan DOM, jadi wajar untuk menggunakan API berbasis acara.
Cukup Tentukan: Pemrograman yang digerakkan oleh peristiwa mengontrol proses aplikasi melalui perubahan dalam peristiwa atau negara. Ini umumnya diimplementasikan melalui pemantauan acara. Setelah acara terdeteksi (mis., Negara berubah), fungsi panggilan balik yang sesuai dipanggil. Kedengarannya akrab? Bahkan, ini adalah prinsip kerja dasar dari loop acara Node.js.
Jika Anda terbiasa dengan pengembangan JavaScript sisi klien, pikirkan tentang metode .on*(), seperti elemen.onClick (), yang digunakan untuk menggabungkan dengan elemen DOM untuk lulus interaksi pengguna. Mode kerja ini memungkinkan beberapa peristiwa dipicu pada satu instance. Node.js memicu mode ini melalui EventeMitter (generator acara), seperti di soket dan modul "http" di sisi server. Satu atau lebih perubahan keadaan dapat dipicu dari satu contoh.
Pola umum lainnya adalah mengungkapkan keberhasilan dan kegagalan. Umumnya ada dua metode implementasi umum sekarang. Hal pertama adalah meneruskan "pengecualian kesalahan" ke dalam panggilan balik, yang umumnya diteruskan ke fungsi callback sebagai parameter pertama. Tipe kedua adalah menggunakan mode desain janji, dan ES6 telah ditambahkan. Catatan* Mode Janji menggunakan metode penulisan rantai fungsi seperti jQuery untuk menghindari sarang fungsi panggilan balik yang dalam, seperti:
Salinan kode adalah sebagai berikut:
$ .getjson ('/getUser'). Done (SuccessHandler) .fail (failhandler)
Modul "FS" (Filesystem) sebagian besar menggunakan gaya lewat pengecualian ke dalam panggilan balik. Secara teknis memicu panggilan tertentu, seperti acara terlampir Fs.readfile (), tetapi API hanya digunakan untuk mengingatkan pengguna untuk mengungkapkan keberhasilan atau kegagalan operasi. API semacam itu dipilih untuk alasan arsitektur, bukan keterbatasan teknis.
Kesalahpahaman yang umum adalah bahwa pemancar peristiwa juga secara inheren tidak sinkron ketika memicu peristiwa, tetapi ini tidak benar. Berikut ini cuplikan kode sederhana untuk membuktikan ini.
Salinan kode adalah sebagai berikut:
fungsi myemitter () {
Eventemitter.call (ini);
}
util.inherit (Myemitter, Eventemitter);
Myemitter.prototype.dostuff = function doStuff () {
Console.log ('Sebelum')
emitter.emit ('api')
Console.log ('After')}
};
var me = myemitter baru ();
me.on ('fire', function () {
console.log ('emit fired');
});
me.dostuff ();
// output:
// sebelum
// memancarkan dipecat
// setelah
CATATAN* Jika emitter.emit asinkron, outputnya harus
// sebelum
// setelah
// memancarkan dipecat
Eventemitter sering dimanifestasikan asinkron karena sering digunakan untuk memberi tahu operasi yang perlu diselesaikan secara tidak sinkron, tetapi API Eventemitter sendiri sepenuhnya sinkron. Fungsi mendengarkan dapat dieksekusi secara tidak sinkron, tetapi harap dicatat bahwa semua fungsi mendengarkan akan dieksekusi secara serempak dalam urutan yang ditambahkan.
Ikhtisar Mekanisme dan Pooling Thread
Node itu sendiri bergantung pada beberapa perpustakaan. Salah satunya adalah Libuv, perpustakaan yang secara ajaib menangani antrian dan eksekusi acara yang asinkron.
Node menggunakan sebanyak mungkin fungsi yang ada untuk memanfaatkan kernel sistem operasi. Misalnya, permintaan respons dihasilkan, koneksi diteruskan dan didelegasikan ke sistem untuk diproses. Misalnya, koneksi yang masuk antri melalui sistem operasi sampai dapat diproses dengan node.
Anda mungkin pernah mendengar bahwa Node memiliki kumpulan utas, dan Anda mungkin bertanya -tanya: "Jika node menangani tugas secara berurutan, mengapa Anda memerlukan kumpulan utas?" Ini karena di kernel, tidak semua tugas dieksekusi secara tidak sinkron. Dalam hal ini, Node.js harus dapat mengunci utas untuk jangka waktu tertentu saat beroperasi sehingga dapat terus menjalankan loop acara tanpa diblokir.
Berikut ini adalah diagram contoh sederhana untuk menunjukkan mekanisme operasi internal:
┌────── Chasan 4irim "" "" "4irim" ""onggol uranding" ─ "
─ ► │ Timesers │
│ └────── Chasan 4irimansirim "" "" ""onggol uranding" ─ "
│ ┌────── Chasan 4irim 4X "tolasanarmaskanirim tolasan tolasanasanaskan tolasanasanaskanaskan tolasanasanaskanaskanaskanaskan 4irim ASirim tolasan tolasanasanaskanaskan" dengan ─┐
│ │ panggilan balik yang tertunda │
│ └────── Chasan 4irim "" "" "tolasanasan tolasanasanaskan tolasanasanaskanaskan tolasanasanaskanaskan unjukat ┌ ┌ olak ilangan ilangan Phalisasiansansansansans balik denganmILarm
│ ┌───── tekstaranding "" "" "" tolasanasanaskanaskanaskan tolasanaskanaskanaskanaskan tolasanasanaskan tolasanasanasanaskan ilangan uman │ │ Masuk: │ │ │ │
│ │ jajak pendapat │◄──┤ koneksi, │
│ └───── tekstar tolasan "" "tolasanasan tolasan tolasanasan tolasanasanaskan ilangan ilangan ilangan harga │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ ┌───── teksIL balik --──── tekstarasan "Miss─irim tolasanasanaskan tolasanasanaskanaskan tolasanarmaskanirim" "" "4irim 4 unjuk olak ilangan ilangan ilangan A r + “──ans─ Ch
──┤ setimmediate │
└────── Chasan 4irim 4 "" "onggol uranding "" tolasan tolasanaskan Manunjuk
Ada beberapa kesulitan dalam memahami mekanisme operasi internal dari loop peristiwa:
Semua callback telah preset melalui proses.nexttick () sebelum akhir satu fase loop peristiwa (misalnya, timer) dan transisi ke fase berikutnya. Ini akan menghindari panggilan rekursif potensial untuk memproses.nexttick (), menyebabkan loop yang tak terbatas.
"Callbacks yang tertunda" adalah panggilan balik dalam antrian panggilan balik yang tidak akan diproses oleh siklus loop peristiwa lainnya (misalnya, diteruskan ke Fs.write).
Pemancar acara dan loop acara
Dengan membuat Eventemitter, interaksi dengan loop acara dapat disederhanakan. Ini adalah enkapsulasi universal yang membuatnya lebih mudah bagi Anda untuk membuat API berbasis acara. Bagaimana keduanya berinteraksi sering membuat pengembang merasa bingung.
Contoh berikut menunjukkan bahwa melupakan bahwa acara tersebut dipicu secara serempak dapat menyebabkan peristiwa tersebut dilewatkan.
Salinan kode adalah sebagai berikut:
// Setelah v0.10, membutuhkan ('acara'). Eventemitter tidak lagi diperlukan
var eventemitter = membutuhkan ('acara');
var util = membutuhkan ('util');
function mything () {
Eventemitter.call (ini);
dofirstthing ();
this.emit ('thing1');
}
util.inherit (Mything, Eventemitter);
var mt = mything baru ();
mt.on ('thing1', function onthing1 () {
// Maaf, kejadian ini tidak akan pernah terjadi
});
Acara 'Thing1' di atas tidak akan pernah ditangkap oleh Mything (), karena Mything () harus dipakai sebelum dapat mendengarkan acara. Berikut ini adalah solusi sederhana tanpa harus menambahkan penutupan tambahan:
Salinan kode adalah sebagai berikut:
var eventemitter = membutuhkan ('acara');
var util = membutuhkan ('util');
function mything () {
Eventemitter.call (ini);
dofirstthing ();
setimmediate (emitthing1, this);
}
util.inherit (Mything, Eventemitter);
fungsi emitthing1 (self) {
self.emit ('thing1');
}
var mt = mything baru ();
mt.on ('thing1', function onthing1 () {
// Jalankan
});
Skema berikut juga dapat bekerja, tetapi beberapa kinerja hilang:
Salinan kode adalah sebagai berikut:
function mything () {
Eventemitter.call (ini);
dofirstthing ();
// Menggunakan fungsi#bind () akan kehilangan kinerja
setimmediate (this.emit.bind (this, 'thing1'));
}
util.inherit (Mything, Eventemitter);
Masalah lain adalah memicu kesalahan (pengecualian). Sudah sulit untuk mengetahui masalah dalam aplikasi Anda, tetapi tanpa tumpukan panggilan (Catatan *E.Stack), debugging hampir tidak mungkin. Tumpukan panggilan akan hilang ketika kesalahan diminta oleh remote secara asinkron. Ada dua solusi yang mungkin: pemicu sinkron atau memastikan bahwa kesalahan dilewati dengan informasi penting lainnya. Contoh berikut menunjukkan kedua solusi ini:
Salinan kode adalah sebagai berikut:
Mything.prototype.foo = function foo () {
// Kesalahan ini akan dipicu secara tidak sinkron
var er = dofirstthing ();
if (er) {
// Saat memicu, Anda perlu membuat kesalahan baru yang mempertahankan informasi tumpukan panggilan di tempat.
setimmediate (emiteror, ini, kesalahan baru ('hal buruk'));
kembali;
}
// pemicu kesalahan dan proses segera (sinkronisasi)
var er = dosecondthing ();
if (er) {
this.emit ('error', 'lebih banyak hal buruk');
kembali;
}
}
Menilai situasinya. Ketika kesalahan dipicu, dimungkinkan untuk diproses segera. Atau, mungkin sepele dan dapat dengan mudah ditangani, atau ditangani kemudian. Selain itu, melewati kesalahan melalui konstruktor bukanlah ide yang baik, karena instance objek yang dibangun cenderung tidak lengkap. Pengecualian adalah kasus di mana kesalahan itu langsung dilemparkan sekarang.
Kesimpulan
Artikel ini secara singkat mengeksplorasi mekanisme operasi internal dan rincian teknis dari loop acara. Semua dipertimbangkan dengan cermat. Artikel lain akan membahas interaksi antara loop peristiwa dan kernel sistem dan menunjukkan keajaiban nodej yang berjalan secara tidak sinkron.