摘要:而事件循環是主線程中執行棧里的代碼執行完畢之后,才開始執行的。由此產生的異步事件執行會作為任務隊列掛在當前循環的末尾執行。在下,觀察者基于監聽事件的完成情況在下基于多線程創建。
主要問題:
1、JS引擎是單線程,如何完成事件循環的? 2、定時器函數為什么計時不準確? 3、回調與異步,有什么聯系和不同? 4、ES6的事件循環有什么變化?Node中呢? 5、異步控制有什么難點?有什么解決方案?
二、事件隊列循環
(一)、瀏覽器線程
JavaScript引擎是基于事件驅動單線程運行的,瀏覽器無論在什么時候都只且只有一個線程在運行JavaScript程序,等待著任務隊列中任務的到來,然后加以處理。
瀏覽器的內核是多線程的,它們在內核控制下相互配合協作,一個瀏覽器至少實現三個常駐線程:JavaScript引擎線程,GUI渲染線程,瀏覽器事件線程。
GUI渲染線程負責渲染瀏覽器界面,當界面需要重繪(Repaint)或由于某種操作引發回流(Reflow)時,該線程就會執行。GUI渲染線程與JavaScript引擎是互斥的,當JavaScript引擎執行時GUI線程會被掛起,GUI更新會被保存在一個隊列中等到JavaScript引擎空閑時立即被執行。所以渲染操作的消耗特別大。
(一)、事件類型與隊列
事件循環:引擎會創建一個類似于 while (true) 的無限循環,每執行一次循環體的過程稱之為 Tick。每次 Tick 的過程就是查看是否有待處理事件,如果有則取出相關事件及回調函數放入執行棧中由主線程執行。待處理的事件會存儲在一個任務隊列中,也就是每次 Tick 會查看任務隊列中是否有需要執行的任務。
任務隊列:異步操作會將相關回調添加到任務隊列中。而不同的異步操作添加到任務隊列的時機也不同,如 onclick, setTimeout, ajax 處理的方式都不同,這些異步操作是由瀏覽器內核的 webcore 來執行的,webcore 包含上圖中的3種 webAPI,分別是 DOM Binding、network、timer模塊。
onclick 由瀏覽器內核的 DOM Binding 模塊來處理,當事件觸發的時候,回調函數會立即添加到任務隊列中。
setTimeout 會由瀏覽器內核的 timer 模塊來進行延時處理,當時間到達的時候,才會將回調函數添加到任務隊列中。
js定時器不準確的原因: 1、異步函數的回調執行會阻塞下一個循環tick。2、瀏覽器的時間精度不一。
ajax 則會由瀏覽器內核的 network 模塊來處理,在網絡請求完成返回之后,才將回調添加到任務隊列中。
主線程:JS 只有一個線程,稱之為主線程。而事件循環是主線程中執行棧里的代碼執行完畢之后,才開始執行的。所以,主線程中要執行的代碼時間過長,會阻塞事件循環的執行,也就會阻塞異步操作的執行。只有當主線程中執行棧為空的時候(即同步代碼執行完后),才會進行事件循環來觀察要執行的事件回調,當事件循環檢測到任務隊列中有事件就取出相關回調放入執行棧中由主線程執行。
在這個過程提高頁面響應速度的方式:減少主線程同步代碼的數量, 將不重要的代碼轉移到事件循環階段執行
(三)、ES6的任務隊列
對事件隊列進行了改造,使得異步回調可以更早的執行,每個tick間隙都會優先執行任務隊列,不用排到事件隊列的末尾。一道必考題,看一下執行順序:
Promise實例具有三種狀態:等待,決議,拒絕。由此產生的異步事件執行會作為任務隊列掛在當前tick循環的末尾執行。
(四)、Nodo中的事件循環
JS引擎的事件循環都需要宿主環境提供隊列維護,Node脫離了瀏覽器,用到了不同的方式和底層系統做交互。
觀察者:引擎在每個循環過程中詢問觀察者是否有要處理的事件。
在Window下,觀察者基于IOCP監聽事件的完成情況;在*nix下基于多線程創建。
在node增加了異步執行的api,如process。nextTick()和setImmiediate。
process。nextTick()的回調函數保存在一個數組中,setImmiediate則是保存在一個鏈表中。
在每輪循環中,會將nextTick的數組中回調函數全部執行完,然后執行一個setImmiediate鏈表中的回調。
(五)、異步事件的處理
異步事件的處理過程中有執行時間、順序不確定,回調地獄,錯誤難捕獲定位等問題。
1、jQuery的 Deferred隊列模塊
JQ在 Deferred隊列模塊的基礎上,模擬實現了promise相似的方法。可以做到鏈式回調,延遲訪問。
2、ES6 Promise
promise實例決議后就不可更改,解決了信任問題。可以使用鏈式回調,隨時訪問異步事件的狀態。相比之前要將回調交給另一方控制,promise可以將回調的執行控制在自己的邏輯中。至此仍未從實質上解決異步。
3、Generator
生成器函數實現了真正的異步控制,可以切換執行環境,并在執行環境之間傳遞變量,實現協作的函數線程。
著名的co模塊,結合使用Promise和Generator實現了異步流程的同步編碼形式。
4、async 函數
ES2017 標準引入了 async 函數,使得異步操作變得更加方便。就是 Generator 函數的語法糖。
Node框架Koa采用了最新的async處理異步流程,使得編碼更加簡潔流暢。
推薦參考書:
《深入淺出NodeJS》 《jQuery技術內幕》 《Webkit技術內幕》 《你不知道的JavaScript》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107393.html
摘要:例如處理請求的線程處理事件的線程定時器線程讀寫文件的線程例如在中等等。事件循環事件循環是指主線程重復從消息隊列中取消息執行的過程。事件觸發時,表示異步任務完成,會將事件監聽器函數封裝成一條消息放到消息隊列中,等待主線程執行。 一. 單線程 我們常說JavaScript是單線程的。 所謂單線程,是指在JS引擎中負責解釋和執行JavaScript代碼的線程只有一個。不妨叫它主線程。 但是實...
摘要:異步那些事一基礎知識異步那些事二分布式事件異步那些事三異步那些事四異步那些事五異步腳本加載事件概念異步回調首先了講講中兩個方法和定義和用法方法用于在指定的毫秒數后調用函數或計算表達式。功能在事件循環的下一次循環中調用回調函數。 JS異步那些事 一 (基礎知識)JS異步那些事 二 (分布式事件)JS異步那些事 三 (Promise)JS異步那些事 四(HTML 5 Web Workers...
摘要:如果對語法分析和預編譯,還有疑問引擎執行的過程的理解語法分析和預編譯階段。參與執行過程的線程分別是引擎線程也稱為內核,負責解析執行腳本程序的主線程例如引擎。以上便是引擎執行宏任務的整個過程。 一、概述 js引擎執行過程主要分為三個階段,分別是語法分析,預編譯和執行階段,上篇文章我們介紹了語法分析和預編譯階段,那么我們先做個簡單概括,如下: 1、語法分析: 分別對加載完成的代碼塊進行語法...
摘要:如果對語法分析和預編譯,還有疑問引擎執行的過程的理解語法分析和預編譯階段。參與執行過程的線程分別是引擎線程也稱為內核,負責解析執行腳本程序的主線程例如引擎。以上便是引擎執行宏任務的整個過程。一、概述 js引擎執行過程主要分為三個階段,分別是語法分析,預編譯和執行階段,上篇文章我們介紹了語法分析和預編譯階段,那么我們先做個簡單概括,如下: 1、語法分析: 分別對加載完成的代碼塊進行語法檢驗,語...
摘要:一棧數據結構與不同,中并沒有嚴格意義上區分棧內存與堆內存。引用數據類型的值是保存在堆內存中的對象。不允許直接訪問堆內存中的位置,因此我們不能直接操作對象的堆內存空間。為了更好的搞懂變量對象與堆內存,我們可以結合以下例子與圖解進行理解。 showImg(https://segmentfault.com/img/remote/1460000009784102?w=1240&h=683); ...
閱讀 3488·2023-04-25 20:41
閱讀 2660·2023-04-25 16:40
閱讀 1433·2021-09-23 11:44
閱讀 1252·2021-09-10 10:51
閱讀 1681·2021-09-07 09:59
閱讀 1642·2019-12-27 12:08
閱讀 551·2019-08-30 15:44
閱讀 3334·2019-08-30 11:08