亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

這篇文章是寫給誰的?

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

非阻塞I / O

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

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

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

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

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

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

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

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

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

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

JavaScript運行時包含一個消息隊列,它存儲了需要處理的消息的列表和相關的回調函數(shù)。這些消息是以隊列的形式來響應回調函數(shù)所涉及的外部事件(如鼠標單擊或收到HTTP請求的響應)的。例如,如果用戶單擊一個按鈕,但沒有提供回調函數(shù),那么也沒有消息會被加入隊列。

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

回調函數(shù)的調用在調用棧作為初始化frame(片段),由于JavaScript是單線程的,未來的消息提取和處理因為等待棧的所有調用返回而被停止。后續(xù)(同步)函數(shù)調用會添加新的調用frame到棧(例如,函數(shù)init調用函數(shù)changeColor)。

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

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

隊列附加消息

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

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

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

Web Workers

使用Web Workers允許您能夠將一項費時的操作在一個單獨的線程中執(zhí)行,從而可以釋放主線程去做別的事情。worker(工作線程)包括一個獨立的消息隊列,事件循 環(huán),內存空間獨立于實例化它的原始線程。worker和主線程之間的通信通過消息傳遞,看起來很像我們往常常見的傳統(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-標簽存在:

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

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

關于閉包的

JavaScript對閉包的支持,允許你這樣注冊回調函數(shù),當回調函數(shù)執(zhí)行時,保持了對他們被創(chuàng)建的環(huán)境的訪問(即使回調的執(zhí)行時創(chuàng)建了一個全新的調用棧)。理解我們的回調作為一個不同的消息的一部分被執(zhí)行,而不是創(chuàng)建它的那個會很有意思??纯聪旅娴睦?

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

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

提醒

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

以上就是本文的全部內容,希望對大家的學習有所幫助。

相關文章

最新評論