EventLoop事件循環(huán)

一、先了解javascript為什么是單線程

????|--javascript語言的特點:單線程。
? ? |--線程和進程
? ? ? ? |--進程:運行的程序就是一個進程,比如正在運行的瀏覽器就是一個進程。
? ? ? ? |--線程:程序中獨立運行的代碼段,一個進程由單個或多個線程組成,線程是負責執(zhí)行代碼的。
? ? |--JS為什么單線程?多線程效率多高啊?
? ????? |--1.首先決定單線程的主要原因是js的用途:用戶交互和操作DOM
? ? ? ? |--2.舉個例子:兩個線程,一個線程在DOM節(jié)點添加內容,另一個線程刪除了這個節(jié)點。
? ? ? ? |--3.上述例子瀏覽器應該以哪個線程為準?
? ? ? ? |--綜上:產生了問題。
? ? |--為了避免復雜性,單線程成為了javascript的核心特征。
? ? |--但是為了利用多核CPU的計算能力,HTML5提出了Web Worker標準,允許js創(chuàng)建多個線程,但是子線程? ??
? ? ? ? 完全受主線程控制,且不得操作DOM,新標準并沒有改變javascript單線程本質。

二、同步執(zhí)行和異步執(zhí)行

? ? |--同步執(zhí)行:因為JS語言特點是單線程,即任務是串行的,后一個任務要等前一個任務執(zhí)行完,才能執(zhí)行,這樣在主線程按照順序,串行執(zhí)行的任務稱為同步執(zhí)行任務。
? ? |--異步執(zhí)行:由于類似于Ajax網(wǎng)絡請求、setTimeout時間延遲、DOM事件的用戶監(jiān)護等,這些任務并不消耗CPU,是一種空等,資源浪費,因此出現(xiàn)了異步。通過將任務交給異步處理模塊去處理,主線程的效率能大大的得到提升,可以并行的處理其他操作。當異步處理完成,主線程空閑時間,主線程讀取相應的callback,進行后續(xù)操作,最大程度的利用CPU。因此異步執(zhí)行就是CPU跳過等待,先處理后續(xù)任務(CPU和網(wǎng)絡模塊、timer模塊等并行進行任務)。
? ? |--而為了協(xié)調主線程和異步模塊之間性的工作,就產生了任務隊列和事件循環(huán)。

三、Event Loop事件循環(huán)機制

事件循環(huán)機制

? ? |--逐步分析事件循環(huán)機制:
? ? ? ? |--1.主線程運行js代碼,產生了堆和執(zhí)行棧。
? ? ? ? ? ? |--堆:對象被分配在堆中,即用來表示大部分非機構化的內存區(qū)域。
? ? ? ? ? ? |--執(zhí)行棧:execution context stack,運行同步代碼。執(zhí)行棧中的代碼(同步任務),總是在讀取
????????????task queue(異步任務)之前。
? ? ? ? |--2.主線程遇到異步任務,指給對應的異步處理模塊(異步進程)進行處理(web API)
? ? ? ? |--3.異步進程處理完畢(Ajax返回、DOM事件處理、Timer到時等),將相應的異步任務推入任務隊列。
? ? ? ? ? ? |--任務隊列:任務隊列是一個事件隊列,IO設備完成一項任務,就在task queue里添加一個事件,表示
? ? ? ? ? ? 相關的異步任務可以進入“執(zhí)行?!绷恕V骶€程讀取任務隊列,就是讀取里面的那些事件(數(shù)據(jù)結構是
? ? ? ? ? ? 先進先出)。主線程的讀取過程是自動的,只要執(zhí)行棧一清空,任務隊列中的第一個事件就會自動
? ? ? ? ? ? 進入主線程。
? ? ? ? ? ? |--回調函數(shù):任務隊列也被稱為callback queue(回調隊列),即異步任務必須指定回調函數(shù),當主線程
? ? ? ? ? ? 開始執(zhí)行異步任務時,就是執(zhí)行對應的回到函數(shù)。
? ? ? ? ? ? |--異步進程包括:
? ? ? ? ? ? ? ? |--類似于onclick,由瀏覽器內核的DOM binding模塊處理,事件觸發(fā)時,回調函數(shù)添加到任務隊列。
? ? ? ? ? ? ? ? |--setTimeout,由瀏覽器內核的Timer模塊處理,事件到達,回調函數(shù)添加到任務隊列。
? ? ? ? ? ? ? ? |--Ajax,由瀏覽器內核的Network模塊處理,網(wǎng)絡請求返回后,添加到任務隊列。
? ? ? ? |--4.主線程循環(huán)的去查找、執(zhí)行任務隊列中的事件,就形成是事件循環(huán)(event loop)。

四、宏任務和微任務

? ? |--將任務進行更精細的定義,分為宏任務和微任務。
? ? ? ? |--宏任務隊列(macrotask queue)?
? ? ? ? ? ? |--不唯一,存在一定的優(yōu)先級(用戶I/O部分優(yōu)先級更高),異步執(zhí)行,同一個事件循環(huán)中只執(zhí)行一個,
? ? ? ? ? ? 包括:整體代碼script,setTimeout、setInterval、ajax、dom操作。
? ? ? ? |--微任務隊列(microtask queue)
? ? ? ? ? ? |--整個事件循環(huán)當中僅存在一個,執(zhí)行為同步,同一個事件循環(huán)中的microtask會按照隊列順序,
? ? ? ? ? ? 串行執(zhí)行。微任務指的是ES6中的Promise。
? ? |--宏任務和微任務區(qū)別:
? ? ? ? |--微任務中所有的callback處在同一個事件循環(huán)中,宏任務中的callback有自己的事件循環(huán)。
? ? ? ? |--利用微任務可以形成一個同步執(zhí)行環(huán)境,但是如果微任務太長,將導致宏任務等待太久,長時間
? ? ? ? 執(zhí)行不了,最終導致用戶的I/O無響應,所以慎用使用。
? ? |--加入宏任務和微任務概念的js運行機制:
? ? ? ? |--1.“執(zhí)行?!弊钕葓?zhí)行所有的同步代碼(宏任務)。執(zhí)行完畢。
? ? ? ? |--2.檢查是否有微任務(microtask),如果有執(zhí)行所有微任務。
? ? ? ? |--3.取出“任務隊列”中的事件對應的回調函數(shù)(宏任務)進入執(zhí)行棧。執(zhí)行完畢。
? ? ? ? |--4.再檢查是否有微任務,有的話執(zhí)行多有微任務。
? ? ? ? |--5.主線程不斷重復執(zhí)行3,4形成事件循環(huán)。
? ? |--示例:

宏任務、微任務示例

? ? |--運行機制分析:
? ? ? ? |--同步環(huán)境:1 -> 2 -> 3
? ? ? ? |--事件循環(huán)(微任務):5
? ? ? ? |--事件循環(huán)(宏任務):4

五、宏任務之setTimeout和setInterval

? ? |--兩個都是定時器,內部運行機制完全相同,區(qū)別在意setTimeout一次性執(zhí)行,而setInterval反復執(zhí)行。
? ? |--setTimeout和setInterval產生的任務都是異步任務,且是宏任務。
? ? |--兩個定時器都是接受兩個參數(shù)
? ? ? ? |--fn:callback 函數(shù)
? ? ? ? |--time:推遲執(zhí)行的毫秒數(shù)、反復執(zhí)行的毫秒數(shù)。
? ? |--注意:如果第二個參數(shù)為0,并不是立即執(zhí)行,而是指定某個任務在主線程最早可得的空閑時間執(zhí)行,
? ? 也就是“盡早執(zhí)行”。它在任務隊列的“尾部”添加一個事件,因此要等同步任務和“任務隊列”現(xiàn)有的事件處理完
? ? 才能得到執(zhí)行。
? ? |--setTimeout(function(){},3000)是異步任務,先被放入event table,3秒后才被推入到task queue,
? ? 而task queue任務隊列里的任務,只有主線程空閑時,才會執(zhí)行。這樣一來如果同步任務超出了
? ? 推遲執(zhí)行的事件3000s,那么這個3000秒就沒有什么意義了,setTimeout(function(){},3000)就等同于
? ? setTimeout(function(){},0)。

六、微任務之Promise

? ? |--當new Promise(function(){... ...}).then(microTask),Promise里的function會立即執(zhí)行,但是then方法里的
? ? 函數(shù)是在執(zhí)行棧后,任務隊列之前執(zhí)行的,即微任務。

七、nodeJs中的process.nextTick

? ? |--node.js中提供的和“任務隊列”有關的方法,他產生的任務是放在執(zhí)行棧的尾部,并不屬于宏任務或
? ? 微任務。因此它的任務總是發(fā)生在異步任務的前面。

八、nodeJs中的setImmediate

? ? |--他產生的任務在“任務隊列”的尾部。

九、總結

? ? |--任務執(zhí)行的優(yōu)先級:
? ? ? ? |--1.同步代碼(宏任務)
? ? ? ? |--2.process.nextTick(node.js中的方法)
? ? ? ? |--3.Promise(微任務)
? ? ? ? |--4.setTimeout(fn)、setInterval(fn)等(宏任務)
? ? ? ? |--5.setImmediate(nodejs)
? ? ? ? |--6.setTimeout(fn,time>0)、setInterval (fn,time>0)
? ? |--示例1:同步任務>異步任務 [優(yōu)先級]

同步>異步

? ? |--示例2:證明任務隊列中,先進先出。

先進先出

? ? |--示例3:在fn()之前增加一個微任務,Promise里的內容同步執(zhí)行,先輸出8,9后才是6。

Promise里的內容同步執(zhí)行

? ? |--示例4:Promise形成了同步執(zhí)行環(huán)境。但是then里的內容會在同步后,任務隊列前執(zhí)行。

then同步后,task queue前

十、擴展

? ? |--案例:for循環(huán)+setTimeout問題? ??

? ? |--如何讓其輸出0~4?
? ? |--解決辦法1:var該為let

let解決

? ? |--解決辦法2:通過閉包

閉包解決

? ? |--解決辦法3:添加立即執(zhí)行的函數(shù)

函數(shù)解決
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容