欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

理解JS事件循環(huán)

 更新時(shí)間:2016年01月07日 10:02:37   投稿:lijiao  
這篇文章主要幫助大家理解JS事件循環(huán),深入淺出的講解了javascript事件循環(huán),感興趣的小伙伴們可以參考一下

伴隨著JavaScript這種web瀏覽器腳本語(yǔ)言的普及,對(duì)它的事件驅(qū)動(dòng)交互模型,以及它與Ruby、Python和Java中常見(jiàn)的請(qǐng)求-響應(yīng)模型的區(qū)別有一個(gè)基本了解,對(duì)您是有益的。在這篇文章中,我將解釋一些JavaScript并發(fā)模型的核心概念,包括其事件循環(huán)和消息隊(duì)列,希望能夠提升你對(duì)一種語(yǔ)言的理解,這種語(yǔ)言你可能已經(jīng)在使用但也許并不完全理解。

這篇文章是寫給誰(shuí)的?

這篇文章是針對(duì)在客戶端或服務(wù)器端使用或計(jì)劃使用JavaScript的web開(kāi)發(fā)人員的。如果你已經(jīng)精通事件循環(huán),那么這篇文章的大部分對(duì)你來(lái)說(shuō)會(huì)很熟悉。對(duì)于那些還不是很精通的人,我希望能給你提供一個(gè)基本的了解,這樣可以更好地幫助你閱讀和編寫日常代碼。

非阻塞I / O

在JavaScript中,幾乎所有的I/O都是非阻塞的。這包括HTTP請(qǐng)求,數(shù)據(jù)庫(kù)操作和磁盤讀寫,單線程執(zhí)行要求在運(yùn)行期執(zhí)行一個(gè)操作時(shí),提供一個(gè)回調(diào)函數(shù),然后繼續(xù)做其它的事情。當(dāng)操作已經(jīng)完成時(shí),消息和已提供的回調(diào)函數(shù)一起插入到隊(duì)列。在將來(lái)的某個(gè)時(shí)候,消息從隊(duì)列移除,回調(diào)函數(shù)觸發(fā)。

雖然這種交互模型可能對(duì)已經(jīng)習(xí)慣使用用戶界面的開(kāi)發(fā)人員很熟悉,比如“mousedown,”和“click”事件在某一時(shí)刻被觸發(fā)。這與通常在服務(wù)器端應(yīng)用程序進(jìn)行的同步式請(qǐng)求-響應(yīng)模型是不同的。

讓我們來(lái)比較一下兩小塊代碼,發(fā)出HTTP請(qǐng)求到www.google.com和輸出響應(yīng)到控制臺(tái)。首先看看Ruby,配合使用Faraday(一個(gè)Ruby 的HTTP 客戶端開(kāi)發(fā)庫(kù)):

response = Faraday.get 'http://www.google.com'
puts response
puts 'Done!'

執(zhí)行路徑很容易跟蹤:

1、執(zhí)行g(shù)et方法,執(zhí)行的線程等待,直到收到響應(yīng)
2、從谷歌收到響應(yīng)并返回給調(diào)用者,它存儲(chǔ)在一個(gè)變量中
3、變量的值(在本例中,就是我們的響應(yīng))輸出到控制臺(tái)
4、值“Done!“輸出到控制臺(tái)
讓我們使用Node.js和Request庫(kù)在JavaScript做同樣的事情:

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

表面上看略有不同,實(shí)際行為截然不同:

1、執(zhí)行請(qǐng)求函數(shù),傳遞一個(gè)匿名函數(shù)作為回調(diào),當(dāng)響應(yīng)在將來(lái)某個(gè)時(shí)候可用時(shí)執(zhí)行回調(diào)。
2、“Done!“立即輸出到控制臺(tái)
3、在將來(lái)的某個(gè)時(shí)候,響應(yīng)返回和回調(diào)執(zhí)行時(shí),輸出它的內(nèi)容到控制臺(tái)
事件循環(huán)

將調(diào)用者和響應(yīng)解耦,使得JavaScript在運(yùn)行期等待異步操作完成和回調(diào)觸發(fā)時(shí)可以做其他事情。但是這些回調(diào)在內(nèi)存中是如何組織的,按什么順序執(zhí)行?什么導(dǎo)致他們被調(diào)用?

JavaScript運(yùn)行時(shí)包含一個(gè)消息隊(duì)列,它存儲(chǔ)了需要處理的消息的列表和相關(guān)的回調(diào)函數(shù)。這些消息是以隊(duì)列的形式來(lái)響應(yīng)回調(diào)函數(shù)所涉及的外部事件(如鼠標(biāo)單擊或收到HTTP請(qǐng)求的響應(yīng))的。例如,如果用戶單擊一個(gè)按鈕,但沒(méi)有提供回調(diào)函數(shù),那么也沒(méi)有消息會(huì)被加入隊(duì)列。

在一次循環(huán),隊(duì)列提取下一條消息(每次提取稱為一次“tick”),當(dāng)事件發(fā)生,該消息的回調(diào)執(zhí)行。

回調(diào)函數(shù)的調(diào)用在調(diào)用棧作為初始化frame(片段),由于JavaScript是單線程的,未來(lái)的消息提取和處理因?yàn)榈却龡5乃姓{(diào)用返回而被停止。后續(xù)(同步)函數(shù)調(diào)用會(huì)添加新的調(diào)用frame到棧(例如,函數(shù)init調(diào)用函數(shù)changeColor)。

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

在這個(gè)例子中,當(dāng)用戶單擊“foo”元素時(shí),一條消息(及其回調(diào)函數(shù)changeColor)會(huì)被插入到隊(duì)列,并觸發(fā)“onclick“事件。當(dāng)消息離開(kāi)隊(duì)列時(shí),其回調(diào)函數(shù)changeColor被調(diào)用。當(dāng)changeColor返回(或者是拋出一個(gè)錯(cuò)誤),事件循環(huán)仍在繼續(xù)。只要函數(shù)changeColor存在,并指定為“foo”元素的onclick方法的回調(diào),那么在該元素上單擊會(huì)導(dǎo)致更多的消息(和相關(guān)的回調(diào)changeColor)插入隊(duì)列。

隊(duì)列附加消息

如果一個(gè)函數(shù)在代碼中按異步調(diào)用(比如setTimeout),提供的回調(diào)將最終作為一個(gè)不同的消息隊(duì)列的一部分被執(zhí)行,它將發(fā)生在事件循環(huán)的某個(gè)未來(lái)的動(dòng)作上。例如:

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

由于setTimeout的非阻塞特性,它的回調(diào)將在至少0毫秒后觸發(fā),而不是作為消息的一部分被處理。在這個(gè)示例中,setTimeout被調(diào)用, 傳入了一個(gè)回調(diào)函數(shù)g且延時(shí)0毫秒后執(zhí)行。當(dāng)我們指定時(shí)間到達(dá)(當(dāng)前情況是,幾乎立即執(zhí)行),一個(gè)單獨(dú)的消息將被加入隊(duì)列(g作為回調(diào)函數(shù))??刂婆_(tái)打印的結(jié)果會(huì)是像這樣:“foo”,“baz”,“blix”,然后是事件循環(huán)的下一個(gè)動(dòng)作:“bar”。如果在同一個(gè)調(diào)用片段中,兩個(gè)調(diào)用都設(shè)置為setTimeout -傳遞給第二個(gè)參數(shù)的值也相同-則它們的回調(diào)將按照調(diào)用順序插入隊(duì)列。

Web Workers

使用Web Workers允許您能夠?qū)⒁豁?xiàng)費(fèi)時(shí)的操作在一個(gè)單獨(dú)的線程中執(zhí)行,從而可以釋放主線程去做別的事情。worker(工作線程)包括一個(gè)獨(dú)立的消息隊(duì)列,事件循 環(huán),內(nèi)存空間獨(dú)立于實(shí)例化它的原始線程。worker和主線程之間的通信通過(guò)消息傳遞,看起來(lái)很像我們往常常見(jiàn)的傳統(tǒng)事件代碼示例。

首先,我們的worker:

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

然后,主要的代碼塊在我們的HTML中以script-標(biāo)簽存在:

// 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);

在這個(gè)例子中,主線程創(chuàng)建一個(gè)worker,同時(shí)注冊(cè)logResult回調(diào)函數(shù)到其“消息”事件。在worker里,reportResult函數(shù)注冊(cè)到自己的“消息”事件中。當(dāng)worker線程接收到主線程的消息,worker入隊(duì)一條消息同時(shí)帶上reportResult回調(diào)函數(shù)。消息出隊(duì)時(shí),一條新消息發(fā)送回主線程,新消息入隊(duì)主線程隊(duì)列(帶上logResult回調(diào)函數(shù))。這樣,開(kāi)發(fā)人員可以將cpu密集型操作委托給一個(gè)單獨(dú)的線程,使主線程解放出來(lái)繼續(xù)處理消息和事件。

關(guān)于閉包的

JavaScript對(duì)閉包的支持,允許你這樣注冊(cè)回調(diào)函數(shù),當(dāng)回調(diào)函數(shù)執(zhí)行時(shí),保持了對(duì)他們被創(chuàng)建的環(huán)境的訪問(wèn)(即使回調(diào)的執(zhí)行時(shí)創(chuàng)建了一個(gè)全新的調(diào)用棧)。理解我們的回調(diào)作為一個(gè)不同的消息的一部分被執(zhí)行,而不是創(chuàng)建它的那個(gè)會(huì)很有意思。看看下面的例子:

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

在這個(gè)例子中,changeHeaderDeferred函數(shù)被執(zhí)行時(shí)包含了變量header。函數(shù) setTimeout被調(diào)用,導(dǎo)致消息(帶上changeHeader回調(diào))被添加到消息隊(duì)列,在大約100毫秒后執(zhí)行。然后 changeHeaderDeferred函數(shù)返回false,結(jié)束第一個(gè)消息的處理,但header變量仍然可以通過(guò)閉包被引用,而不是被垃圾回收。當(dāng) 第二個(gè)消息被處理(changeHeader函數(shù)),它保持了對(duì)在外部函數(shù)作用域中聲明的header變量的訪問(wèn)。一旦第二個(gè)消息 (changeHeader函數(shù))執(zhí)行結(jié)束,header變量可以被垃圾回收。

提醒

JavaScript 事件驅(qū)動(dòng)的交互模型不同于許多程序員習(xí)慣的請(qǐng)求-響應(yīng)模型,但如你所見(jiàn),它并不復(fù)雜。使用簡(jiǎn)單的消息隊(duì)列和事件循環(huán),JavaScript使得開(kāi)發(fā)人員在構(gòu)建他們的系統(tǒng)時(shí)使用大量asynchronously-fired(異步-觸發(fā))回調(diào)函數(shù),讓運(yùn)行時(shí)環(huán)境能在等待外部事件觸發(fā)的同時(shí)處理并發(fā)操作。然而,這不過(guò)是并發(fā)的一種方法。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。

相關(guān)文章

最新評(píng)論