Dalam modul pemrograman tradisional, operasi I/O seperti panggilan fungsi lokal biasa: program diblokir sebelum fungsi dieksekusi dan tidak dapat terus berjalan. I/O yang diblokir berasal dari model Slice waktu sebelumnya, di mana setiap proses seperti orang mandiri, dengan tujuan membedakan semua orang, dan semua orang biasanya hanya dapat melakukan satu hal pada saat yang sama, dan harus menunggu hal sebelumnya dilakukan sebelum memutuskan apa yang harus dilakukan selanjutnya. Namun, model "satu pengguna, satu proses" yang banyak digunakan pada jaringan komputer dan internet sangat terukur. Saat mengelola banyak proses, ia mengkonsumsi banyak memori dan switching konteks juga akan menempati banyak sumber daya. Ini adalah beban besar pada sistem operasi, dan ketika jumlah proses meningkat, kinerja sistem akan membusuk dengan tajam.
Multithreading adalah alternatif. Utas adalah proses ringan yang berbagi memori dengan utas lain dalam proses yang sama. Ini lebih seperti perpanjangan dari model tradisional, yang digunakan untuk menjalankan beberapa utas secara bersamaan. Ketika satu utas sedang menunggu operasi I/O, utas lain dapat mengambil alih CPU. Ketika operasi I/O selesai, utas yang menunggu di depan akan terbangun. Dengan kata lain, utas yang berjalan dapat terganggu dan kemudian dilanjutkan nanti. Selain itu, utas dapat berjalan secara paralel di bawah core CPU multi-core yang berbeda di bawah beberapa sistem.
Pemrogram tidak tahu jam berapa utas akan berjalan. Mereka harus berhati -hati untuk menangani akses bersamaan ke memori bersama, sehingga mereka harus menggunakan beberapa primitif sinkronisasi untuk menyinkronkan akses ke struktur data tertentu, seperti menggunakan kunci atau semafor, untuk memaksa utas untuk dieksekusi dalam perilaku dan rencana tertentu. Aplikasi yang sangat bergantung pada keadaan bersama antara utas dapat dengan mudah memiliki beberapa masalah aneh dengan keacakan dan kesulitan yang kuat dalam menemukan.
Cara lain adalah dengan menggunakan kolaborasi multi-threaded, di mana Anda bertanggung jawab untuk secara eksplisit melepaskan CPU dan menyerahkan waktu CPU ke utas lain. Karena Anda secara pribadi mengendalikan rencana eksekusi utas, kebutuhan akan sinkronisasi berkurang, tetapi juga meningkatkan kompleksitas program dan kemungkinan kesalahan, dan tidak menghindari masalah multi-threading.
Apa pemrograman yang didorong oleh acara
Pemrograman yang digerakkan oleh peristiwa adalah gaya pemrograman, di mana peristiwa menentukan proses eksekusi suatu program. Acara ditangani oleh penangan acara atau panggilan balik acara. Callback acara adalah fungsi yang dipanggil ketika peristiwa tertentu terjadi, seperti database mengembalikan hasil kueri atau pengguna mengklik tombol.
Ingatlah bahwa dalam mode pemrograman I/O tradisional yang diblokir, kueri basis data mungkin terlihat seperti ini:
Salinan kode adalah sebagai berikut:
hasil = kueri ('pilih * dari posting di mana id = 1');
do_something_with (hasil);
Fungsi kueri di atas akan menjaga utas atau proses saat ini dalam keadaan menunggu sampai database yang mendasarinya melengkapi operasi kueri dan kembali.
Dalam model yang digerakkan oleh acara, kueri ini akan menjadi seperti ini:
Salinan kode adalah sebagai berikut:
query_finished = fungsi (hasil) {
do_something_with (hasil);
}
kueri ('pilih * dari posting di mana id = 1', query_finished);
Pertama, Anda mendefinisikan fungsi yang disebut query_finished, yang berisi apa yang harus dilakukan setelah kueri selesai. Kemudian lulus fungsi ini sebagai parameter ke fungsi kueri. Query_finished akan dipanggil setelah eksekusi kueri, bukan hanya mengembalikan hasil kueri.
Ketika suatu peristiwa yang Anda minati terjadi, fungsi yang Anda definisikan akan dipanggil alih -alih hanya mengembalikan nilai hasilnya. Model pemrograman ini disebut pemrograman yang digerakkan oleh peristiwa atau pemrograman asinkron. Ini adalah salah satu fitur node yang paling jelas. Model pemrograman ini berarti bahwa proses saat ini tidak akan diblokir saat menjalankan operasi I/O. Oleh karena itu, beberapa operasi I/O dapat dieksekusi secara paralel, dan fungsi panggilan balik yang sesuai akan dipanggil setelah operasi selesai.
Lapisan yang mendasari pemrograman yang digerakkan oleh acara bergantung pada loop acara. Loop peristiwa pada dasarnya adalah struktur di mana deteksi peristiwa dan prosesor acara memicu panggilan loop berkelanjutan dari dua fungsi ini. Di setiap loop, mekanisme loop peristiwa perlu mendeteksi peristiwa mana yang terjadi. Ketika peristiwa terjadi, ia menemukan fungsi panggilan balik yang sesuai dan memanggilnya.
Loop acara hanyalah utas yang berjalan dalam proses. Ketika suatu peristiwa terjadi, prosesor acara dapat berjalan sendiri dan tidak akan terganggu, yaitu:
1. Paling -paling satu acara panggilan balik acara berjalan pada saat tertentu
2. Tidak ada prosesor acara yang terganggu saat berlari
Dengan ini, pengembang tidak dapat lagi sakit kepala tentang sinkronisasi utas dan modifikasi bersamaan dari memori bersama.
Rahasia terkenal:
Dahulu kala, orang-orang dalam komunitas pemrograman sistem tahu bahwa pemrograman yang digerakkan oleh peristiwa adalah cara terbaik untuk membuat layanan konkurensi yang tinggi karena tidak harus menghemat banyak konteks, sehingga menghemat banyak memori, tidak banyak saklar konteks, dan menghemat banyak waktu eksekusi.
Perlahan -lahan, konsep ini meresapi platform dan komunitas lain, dan beberapa implementasi loop acara terkenal muncul, seperti mesin acara Ruby, Perl's Anyevnet, dan Python dipelintir. Selain itu, ada banyak implementasi dan bahasa lainnya.
Untuk mengembangkan kerangka kerja ini, Anda perlu mempelajari pengetahuan spesifik yang terkait dengan kerangka kerja dan perpustakaan kelas khusus kerangka kerja. Misalnya, saat menggunakan mesin acara, untuk menikmati manfaat dari non-blocking, Anda harus menghindari penggunaan perpustakaan kelas sinkron dan hanya dapat menggunakan perpustakaan kelas asinkron mesin acara. Jika Anda menggunakan pustaka pemblokiran apa pun (seperti sebagian besar pustaka standar Ruby), server Anda kehilangan skalabilitas optimalnya karena loop acara masih akan diblokir terus -menerus, memblokir pemrosesan acara I/O dari waktu ke waktu.
Node awalnya dirancang sebagai platform server I/O yang tidak memblokir, jadi secara umum, Anda harus mengharapkan semua kode yang berjalan di atasnya menjadi tidak blokir. Karena JavaScript sangat kecil dan tidak memaksa model I/O (karena tidak memiliki perpustakaan kelas I/O standar), simpul dibangun di lingkungan yang sangat murni dan tidak akan ada masalah warisan.
Bagaimana Node dan JavaScript Menyederhanakan Aplikasi Asinkron
Penulis Node Ryan Dahl awalnya menggunakan C untuk mengembangkan proyek ini, tetapi menemukan bahwa konteks pemeliharaan panggilan fungsi terlalu kompleks, menghasilkan kompleksitas kode tinggi. Kemudian dia beralih ke Lua, tetapi Lua sudah memiliki beberapa perpustakaan I/O yang memblokir. Pencampuran pemblokiran dan non-pemblokiran dapat membingungkan pengembang dan dengan demikian mencegah banyak orang untuk membangun aplikasi yang dapat diskalakan. Oleh karena itu, Lua juga ditinggalkan oleh Dahl. Akhirnya dia beralih ke JavaScript, penutupan di JavaScript dan fungsi objek tingkat pertama, yang membuat JavaScript sangat cocok untuk pemrograman yang digerakkan oleh peristiwa. Keajaiban Javascript adalah salah satu alasan utama mengapa Node sangat populer.
Apa itu penutupan
Penutupan dapat dipahami sebagai fungsi khusus, tetapi dapat mewarisi dan mengakses variabel dalam ruang lingkup yang didefinisikan. Saat Anda melewati fungsi panggilan balik sebagai parameter ke fungsi lain, itu akan dipanggil nanti. Keajaiban adalah bahwa ketika fungsi panggilan balik ini disebut nanti, ia benar -benar mengingat konteks di mana ia mendefinisikan dirinya sendiri dan variabel dalam konteks induk, dan juga dapat mengaksesnya secara normal. Fitur yang kuat ini adalah inti dari keberhasilan Node.
Contoh berikut akan menunjukkan cara kerja penutupan JavaScript di browser web. Jika Anda ingin mendengarkan acara yang berdiri sendiri pada tombol, Anda dapat melakukan ini:
Salinan kode adalah sebagai berikut:
var clickcount = 0;
document.geteLementById ('mybutton'). onclick = function () {
ClickCount += 1;
peringatan ("diklik" + clickcount + "kali.");
};
Beginilah cara menggunakan jQuery:
Salinan kode adalah sebagai berikut:
var clickcount = 0;
$ ('Tombol#myButton'). Klik (function () {
klikCount ++;
peringatan ('diklik' + clickcount + 'Times.');
});
Dalam JavaScript, fungsi adalah jenis objek pertama, yang berarti Anda dapat melewati fungsi sebagai parameter ke fungsi lain. Dalam dua contoh di atas, yang pertama memberikan fungsi ke fungsi lain, dan yang terakhir melewati fungsi sebagai parameter ke fungsi lain. Fungsi Pemrosesan Acara Klik (fungsi panggilan balik) dapat mengakses setiap variabel di bawah blok kode di mana fungsi mendefinisikannya. Dalam contoh ini, ia dapat mengakses variabel ClickCount yang ditentukan dalam penutupan induknya.
Variabel ClickCount ada di ruang lingkup global (ruang lingkup terluar di JavaScript), yang menghemat berapa kali pengguna mengklik tombol. Biasanya merupakan kebiasaan buruk untuk menyimpan variabel di bawah ruang lingkup global, karena mudah untuk bertentangan dengan kode lain, dan Anda harus menempatkan variabel di ruang lingkup lokal di mana Anda menggunakannya. Sebagian besar waktu, hanya membungkus kode dengan satu fungsi setara dengan menciptakan penutupan lain, yang dapat dengan mudah menghindari mencemari lingkungan global, seperti ini:
Salinan kode adalah sebagai berikut:
(fungsi() {
var clickcount = 0;
$ ('Tombol#myButton'). Klik (function () {
clickcount ++;
peringatan ('diklik' + clickcount + 'Times.');
});
} ());
Catatan: Baris ketujuh dari kode di atas mendefinisikan fungsi dan segera memanggilnya. Ini adalah pola desain umum dalam JavaScript: Buat ruang lingkup baru dengan membuat fungsi.
Bagaimana penutupan membantu pemrograman asinkron
Dalam model pemrograman yang digerakkan oleh peristiwa, pertama-tama tulis kode untuk dijalankan setelah peristiwa terjadi, kemudian masukkan kode ke dalam suatu fungsi, dan akhirnya lulus fungsi sebagai parameter ke penelepon, dan kemudian panggil dengan fungsi penelepon nanti.
Dalam JavaScript, fungsi bukanlah definisi yang terisolasi. Ini juga mengingat konteks ruang lingkup yang dinyatakan. Mekanisme ini memungkinkan fungsi JavaScript untuk mengakses konteks di mana definisi fungsi berada dan semua variabel dalam konteks induk.
Saat Anda melewati fungsi panggilan balik sebagai parameter ke penelepon, fungsi akan dipanggil di beberapa titik selanjutnya. Bahkan jika ruang lingkup yang mendefinisikan fungsi callback telah berakhir, ketika fungsi callback dipanggil, ia masih dapat mengakses semua variabel dalam lingkup akhir dan ruang lingkup induknya. Seperti contoh terakhir, fungsi callback dipanggil di dalam klik () jQuery, tetapi masih dapat mengakses variabel clickcount.
Keajaiban penutupan ditunjukkan sebelumnya. Melewati variabel keadaan ke suatu fungsi memungkinkan Anda untuk melakukan pemrograman yang digerakkan oleh peristiwa tanpa mempertahankan status. Mekanisme penutupan Javascript akan membantu Anda mempertahankannya.
ringkasan
Pemrograman yang digerakkan oleh peristiwa adalah model pemrograman yang menentukan proses eksekusi program melalui pemicu peristiwa. Pemrogram mendaftarkan fungsi panggilan balik untuk acara yang mereka minati (biasanya disebut penangan acara), dan sistem kemudian memanggil penangan acara terdaftar ketika acara terjadi. Model pemrograman ini memiliki banyak keunggulan yang tidak dimiliki oleh model pemrograman pemblokiran tradisional. Di masa lalu, untuk mengimplementasikan fitur serupa, multi-proses/multi-threading harus digunakan.
JavaScript adalah bahasa yang kuat karena jenis fungsi objek dan properti penutupan pertama, membuatnya sangat cocok untuk pemrograman yang digerakkan oleh peristiwa.