Rumah > hujung hadapan web > tutorial js > Memahami kemahiran loop_javascript acara JS

Memahami kemahiran loop_javascript acara JS

WBOY
Lepaskan: 2016-05-16 15:21:26
asal
1611 orang telah melayarinya

Dengan populariti JavaScript, bahasa penskripan penyemak imbas web, adalah berfaedah untuk anda mempunyai pemahaman asas tentang model interaksi dipacu peristiwa dan perbezaannya daripada model respons permintaan biasa dalam Ruby, Python dan Java . Dalam siaran ini, saya akan menerangkan beberapa konsep teras model konkurensi JavaScript, termasuk gelung acara dan baris gilir mesejnya, dengan harapan dapat meningkatkan pemahaman anda tentang bahasa yang mungkin anda sudah gunakan tetapi mungkin tidak faham sepenuhnya.

Untuk siapa artikel ini ditulis?

Artikel ini ditujukan kepada pembangun web yang menggunakan atau merancang untuk menggunakan JavaScript pada sisi klien atau pelayan. Jika anda sudah mahir dalam gelung acara, kebanyakan artikel ini akan anda kenali. Bagi mereka yang belum begitu mahir, saya berharap dapat memberi anda pemahaman asas yang akan membantu anda membaca dan menulis kod dengan lebih baik setiap hari.

I/O tidak menyekat

Dalam JavaScript, hampir semua I/O tidak menyekat. Ini termasuk permintaan HTTP, operasi pangkalan data dan cakera membaca dan menulis pelaksanaan satu-benang memerlukan bahawa apabila operasi dilakukan semasa masa jalan, fungsi panggil balik disediakan dan kemudian terus melakukan perkara lain. Apabila operasi telah selesai, mesej dimasukkan ke dalam baris gilir bersama-sama dengan fungsi panggil balik yang disediakan. Pada satu ketika pada masa hadapan, mesej dialih keluar daripada baris gilir dan fungsi panggil balik diaktifkan.

Walaupun model interaksi ini mungkin biasa bagi pembangun yang sudah biasa bekerja dengan antara muka pengguna, seperti peristiwa "turun tetikus" dan "klik" yang dicetuskan pada satu ketika. Ini berbeza daripada model permintaan-tindak balas segerak yang biasanya dilakukan dalam aplikasi sebelah pelayan.

Mari bandingkan dua keping kod kecil yang membuat permintaan HTTP ke www.google.com dan mencetak respons kepada konsol. Mula-mula, mari kita lihat Ruby dan gunakannya dengan Faraday (pustaka pembangunan klien HTTP Ruby):

response = Faraday.get 'http://www.google.com'
puts response
puts 'Done!'
Salin selepas log masuk

Laluan pelaksanaan mudah dikesan:

1. Jalankan kaedah dapatkan, dan urutan pelaksana menunggu sehingga respons diterima
2. Respons diterima daripada Google dan dikembalikan kepada pemanggil, ia disimpan dalam pembolehubah
3. Nilai pembolehubah (dalam kes ini, respons kami) ialah output kepada konsol
4. Nilai "Selesai!" adalah output kepada konsol
Mari lakukan perkara yang sama dalam JavaScript menggunakan Node.js dan perpustakaan Permintaan:

request('http://www.google.com', function(error, response, body) {
 console.log(body);
});
 
console.log('Done!');
Salin selepas log masuk

Ia kelihatan sedikit berbeza pada permukaan, tetapi tingkah laku sebenar berbeza sama sekali:

1. Laksanakan fungsi permintaan, berikan fungsi tanpa nama sebagai panggilan balik dan laksanakan panggilan balik apabila respons tersedia pada masa hadapan.
2. "Selesai!" dikeluarkan ke konsol serta-merta
3. Pada masa akan datang, apabila respons kembali dan panggilan balik dilaksanakan, keluarkan kandungannya ke konsol
Gelung Acara

Nyahgandingkan pemanggil dan respons supaya JavaScript boleh melakukan perkara lain sementara menunggu operasi tak segerak selesai dan panggilan balik dihidupkan semasa masa jalanan. Tetapi bagaimanakah panggilan balik ini disusun dalam ingatan dan dalam urutan apakah ia dilaksanakan? Apakah yang menyebabkan mereka dipanggil?

Waktu jalan JavaScript mengandungi baris gilir mesej, yang menyimpan senarai mesej yang perlu diproses dan fungsi panggil balik yang berkaitan. Mesej ini dibariskan sebagai tindak balas kepada peristiwa luaran yang terlibat dalam fungsi panggil balik (seperti klik tetikus atau respons kepada permintaan HTTP). Contohnya, jika pengguna mengklik butang dan tiada fungsi panggil balik disediakan, tiada mesej akan dibariskan.

Dalam gelung, baris gilir mengambil mesej seterusnya (setiap pengambilan dipanggil "tanda") dan apabila peristiwa berlaku, panggilan balik mesej itu dilaksanakan.

Panggilan ke fungsi panggil balik bertindak sebagai bingkai permulaan (serpihan) dalam timbunan panggilan Memandangkan JavaScript adalah satu utas, pengekstrakan dan pemprosesan mesej akan datang dihentikan sementara menunggu semua panggilan dalam tindanan kembali. Panggilan fungsi (segerak) berikutnya menambah bingkai panggilan baharu pada timbunan (contohnya, fungsi panggilan init fungsi changeColor).

function init() {
 var link = document.getElementById("foo");
 
 link.addEventListener("click", function changeColor() {
  this.style.color = "burlywood";
 });
}
 
init();
Salin selepas log masuk

Dalam contoh ini, apabila pengguna mengklik elemen "foo", mesej (dan fungsi panggil balik changeColor) akan dimasukkan ke dalam baris gilir dan acara "onclick" akan dicetuskan. Apabila mesej meninggalkan baris gilir, fungsi panggil balik changeColor dipanggil. Apabila changeColor kembali (atau membuang ralat), gelung acara diteruskan. Selagi fungsi changeColor wujud dan dinyatakan sebagai panggilan balik kepada kaedah onclick elemen "foo", kemudian mengklik pada elemen itu akan menyebabkan lebih banyak mesej (dan changeColor panggilan balik yang berkaitan) dimasukkan ke dalam baris gilir.

Mesej tambah giliran

Jika fungsi dipanggil secara tidak segerak dalam kod (seperti setTimeout), panggilan balik yang disediakan akhirnya akan dilaksanakan sebagai sebahagian daripada baris gilir mesej yang berbeza, yang akan berlaku pada beberapa tindakan masa hadapan dalam gelung acara. Contohnya:

function f() {
 console.log("foo");
 setTimeout(g, 0);
 console.log("baz");
 h();
}
 
function g() {
 console.log("bar");
}
 
function h() {
 console.log("blix");
}
 
f();
Salin selepas log masuk

由于setTimeout的非阻塞特性,它的回调将在至少0毫秒后触发,而不是作为消息的一部分被处理。在这个示例中,setTimeout被调用, 传入了一个回调函数g且延时0毫秒后执行。当我们指定时间到达(当前情况是,几乎立即执行),一个单独的消息将被加入队列(g作为回调函数)。控制台打印的结果会是像这样:“foo”,“baz”,“blix”,然后是事件循环的下一个动作:“bar”。如果在同一个调用片段中,两个调用都设置为setTimeout -传递给第二个参数的值也相同-则它们的回调将按照调用顺序插入队列。

Web Workers

使用Web Workers允许您能够将一项费时的操作在一个单独的线程中执行,从而可以释放主线程去做别的事情。worker(工作线程)包括一个独立的消息队列,事件循 环,内存空间独立于实例化它的原始线程。worker和主线程之间的通信通过消息传递,看起来很像我们往常常见的传统事件代码示例。

首先,我们的worker:

// our worker, which does some CPU-intensive operation
var reportResult = function(e) {
 pi = SomeLib.computePiToSpecifiedDecimals(e.data);
 postMessage(pi);
};
 
onmessage = reportResult;
Salin selepas log masuk

然后,主要的代码块在我们的HTML中以script-标签存在:

// our main code, in a <script>-tag in our HTML page
var piWorker = new Worker("pi_calculator.js");
var logResult = function(e) {
 console.log("PI: " + e.data);
};
 
piWorker.addEventListener("message", logResult, false);
piWorker.postMessage(100000);
Salin selepas log masuk

在这个例子中,主线程创建一个worker,同时注册logResult回调函数到其“消息”事件。在worker里,reportResult函数注册到自己的“消息”事件中。当worker线程接收到主线程的消息,worker入队一条消息同时带上reportResult回调函数。消息出队时,一条新消息发送回主线程,新消息入队主线程队列(带上logResult回调函数)。这样,开发人员可以将cpu密集型操作委托给一个单独的线程,使主线程解放出来继续处理消息和事件。

关于闭包的

JavaScript对闭包的支持,允许你这样注册回调函数,当回调函数执行时,保持了对他们被创建的环境的访问(即使回调的执行时创建了一个全新的调用栈)。理解我们的回调作为一个不同的消息的一部分被执行,而不是创建它的那个会很有意思。看看下面的例子:

function changeHeaderDeferred() {
 var header = document.getElementById("header");
 
 setTimeout(function changeHeader() {
  header.style.color = "red";
 
  return false;
 }, 100);
 
 return false;
}
 
changeHeaderDeferred();
Salin selepas log masuk

在这个例子中,changeHeaderDeferred函数被执行时包含了变量header。函数 setTimeout被调用,导致消息(带上changeHeader回调)被添加到消息队列,在大约100毫秒后执行。然后 changeHeaderDeferred函数返回false,结束第一个消息的处理,但header变量仍然可以通过闭包被引用,而不是被垃圾回收。当 第二个消息被处理(changeHeader函数),它保持了对在外部函数作用域中声明的header变量的访问。一旦第二个消息 (changeHeader函数)执行结束,header变量可以被垃圾回收。

提醒

JavaScript 事件驱动的交互模型不同于许多程序员习惯的请求-响应模型,但如你所见,它并不复杂。使用简单的消息队列和事件循环,JavaScript使得开发人员在构建他们的系统时使用大量asynchronously-fired(异步-触发)回调函数,让运行时环境能在等待外部事件触发的同时处理并发操作。然而,这不过是并发的一种方法。

以上就是本文的全部内容,希望对大家的学习有所帮助。

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan