国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JS專題之事件循環

mengera88 / 2265人閱讀

摘要:宏任務需要多次事件循環才能執行完。總結事件循環是和事件調用機制的核心,保證了頁面可以有序無阻塞的進行。事件循環的主要邏輯是先執行調用棧,直到清空調用棧只剩下全局上下文。微任務執行后完,進行頁面渲染和垃圾回收后進行下一輪事件循環。

準備知識
1. 進程(process)

進程是系統資源分配一個獨立單位,一個程序至少有一個進程。比方說:一個工廠代表一個 CPU, 一個車間就是一個進程,任一時刻,只能有一個進程在運行,其他進程處于非運行狀態。

2. 線程(Thread)

線程是CPU調度和分派的基本單位,一個線程只能屬于一個進程,一個進程可以有多個線程且至少有一個。比方說一個車間的工人,可以有多個工人一起工作。

生活中常常能看到,某某電腦 CPU 的 4 核 4 線程,其意思是指,這款 CPU 同一時間最多只能運行 4 個線程,所以有些線程會處于工作狀態,有的線程會處于中斷,堵塞,睡眠狀態。

經常看到有很多任務同時在進行,一邊工作,一邊聽歌,還一邊下載電影。那是因為這些線程在以閃電般的速度不斷的切換主要的幾個線程,所以,人的體驗上感覺是很多很多任務在同時進行。

3. 棧(stack)

棧是一種數據結構,具有后進先出的特點,最開始進入棧結構的數據反而最后才能出來。

4. 隊列(queue)

隊列也是一種數據結構,數據只能從一邊進,一邊出,先進去的自然就先出來。

5. 同步和異步(sync async)

同步和異步關注的消息通信機制,同步在函數調用時,如果調用者沒有拿到響應結果,程序會繼續等待,知道拿到結果為止。而異步會執行其后的代碼,等到有響應結果后,才處理響應。

6. 阻塞和非阻塞(blocking & non-blocking)

阻塞和非阻塞關注的是程序等待調用結果時的狀態,阻塞的意思是,在調用結果返回響應前,線程會被掛起占用,程序無法繼續往下走,而非阻塞的線程則不會掛起,后面的代碼能夠繼續往下執行。

比方說:我去超市買包薯片,老板告訴我貨架上沒貨了,馬上去庫房拿,這過程中,老板要我站著等他,直到他拿到貨出來給我。這個過程就是阻塞。

如果老板告訴我,可以先回去,他一會去庫房拿,拿到了之后打電話給我。這個過程,就是非阻塞的,我不用等待,還可以干其他的事情。

7. 執行棧(execution stack)

js 代碼在執行代碼時,JS 會給調用代碼生成一個執行上下文對象,并將其壓入執行上下文棧,首先進入棧底的是全局上下文,然后是函數的執行上下文(Execution Context),函數執行完之后,函數上下文從棧中彈出,直到退出瀏覽器,全局上下文才從棧底彈出。

用代碼舉個例子:

var globalName = "window";

var foo1 = function() {
    console.log("foo1");
}

var foo2 = function() {
    console.log("foo2");
    foo1();
}

foo2();

上面的圖片大致能夠描述執行上下文棧的實現邏輯,有關執行上下文的知識,大家可以翻看我之前的文章 - 《JavaScript 之執行上下文》

二、為什么 JS 是單線程模型?
JavaScript 的一個非常有趣的特性是事件循環模型,與許多其他語言不同,它永不阻塞。 處理 I/O 通常通過事件和回調來執行  -- MDN

瀏覽器主要任務是給用戶是視覺和交互上的體驗,如果頁面使用過程中,偶爾出現阻塞、掛起、無響應的體驗一定是非常糟糕的。同時,如果采用多線程同步的模型,那么如何保證同一時間修改了 DOM, 到底是哪個線程先生效呢。

瀏覽器執行環境的核心思想在于任務調度方式的特別:

哪個任務的優先級高,先來就先運行,直到執行完了才執行下一個,并且同一時刻只能執行一個代碼片段,即所謂的單線程模型。

比方說,銀行的柜臺只開啟了一個柜臺,每個人想要辦理業務,就得先拿號排隊,叫到了你的號碼,你才能上去辦理業務。不能多個人同時在一個柜臺辦理業務,不然就很容易出差錯。

三、事件循環

事件循環是 JS 處理各種事件的核心,由于多個線程同時操作 DOM, 造成不可控的問題,所以 JS 采用了單線程模型。另外,由于所有的事件同步執行,執行完一個才能執行下一個,會造成頁面渲染的堵塞。JS 中存在異步事件,用戶可以在點擊頁面的時候,請求網絡響應的同事,還可以進行其他的點擊操作,保證了頁面不會因為網絡請求,多種 IO 接口響應慢造成代碼執行的堵塞和掛起。

事件循環的順序是:

進入 script 標簽,創建全局上下文

執行全局上下文中的函數,將其壓入執行調用棧

某個函數執行完后,函數彈出執行棧,清空函數上下文中的變量對象和內存空間,判斷是否需要更新渲染,如果需要則更新渲染。

如果遇到異步事件,也會壓入執行調用棧,但瀏覽器識別到它是異步事件后,會將其彈出執行棧,然后將異步事件的回調函數放入事件隊列中。

執行直到函數調用棧清空只剩全局執行上下文,這時,JS 會檢查事件隊列中是否有事件,如果有,則將事件隊列中的一個事件出隊,然后壓入執行棧中執行。

當執行棧又清空只剩全局執行上下文時,又會重復第 5 步。這就是 JS 的事件循環。

當用戶關閉瀏覽器,全局執行上下文彈出執行棧,清空相應上下文中的變量對象和內存空間。

接下來我們用代碼來解釋:

console.log("script start!");

function foo1() {
    console.log("foo1");
}

foo1();

setTimeout(function () {
    console.log("setTimeout!");
}, 1000);

function foo2() {
    console.log("foo2");
}

foo2();

console.log("script end!");

打印:
// script start!
// foo1
// foo2
// script end!

// setTimeout!

那我們嘗試把 setTimeout 的延遲時間改為 0,想要立即執行,看會不會立即執行:

console.log("script start!");

function foo1() {
    console.log("foo1");
}

foo1();

setTimeout(function () {
    console.log("setTimeout!");
}, 0);

function foo2() {
    console.log("foo2");
}

foo2();

console.log("script end!");

打印:
// script start!
// foo1
// foo2
// script end!
// setTimeout!

可以看出 setTimeout 屬于異步事件,總是會在主線程的任務執行完后才開始執行。

順便說一下事件循環幾個原則:

一次只處理一個任務

一個任務從開始到完成,不會被其他任務所中斷

這兩個原則保證了瀏覽器任務單元的完整性,事件調用的有序性。

四、宏任務和微任務

事件循環的實現本來應該由一個用于宏任務的隊列和一個用于微任務的隊列進行完成,這使得事件循環要根據任務類型來進行優先處理。

宏任務:
宏任務包括:

創建文檔對象、解析 HTML、執行主線程代碼(script)

執行各種事件:頁面加載、輸入、點擊

setTimout,setInterval 異步事件

宏任務代表一個個離散、獨立的工作單元,運行完任務后,瀏覽器可以進行其他的任務調度,如更新渲染或執行垃圾回收。宏任務需要多次事件循環才能執行完。

微任務:
微任務包括:

Promise 回調函數

new MutaionObserver()

微任務是更小的任務,微任務需要盡可能地、通過異步方式執行,微任務更新瀏覽器的狀態,但必須在瀏覽器執行其他任務之前執行。微任務使得我們避免不必要的 UI 重繪。微任務在一次事件循環中必須全部執行完。

宏任務和微任務的執行優先級原則是:

完成一個宏任務后,執行余下的微任務

同一次事件循環中,宏任務永遠在微任務之前執行。

ok,知道了優先級原則后,我們來看一段代碼:

console.log(1);

setTimeout(function() {
    console.log(2);
    new Promise(resolve => {
        console.log(3);
        resolve(4);
        console.log(5);
    }).then(data => {
        console.log(data);
    });
}, 0);

new Promise(resolve => {
    console.log(6);
    resolve(7);
    console.log(8);
}).then(data => {
    console.log(data);
});

setTimeout(function() {
    console.log(9);
}, 0);

console.log(10);

output:  
第一次循環:
// 1
// 6
// 8
// 10
// 7

第二次循環:
// 2
// 3
// 5
// 4


第三次循環
// 9

我們一起來分析以上代碼:

進入第一次事件循環,script 這個宏任務,輸出 1

第一個 setTimeout 函數本身是函數調用,屬于任務源,setTimeout 的回調函數,即第一個參數,才是被分發的任務,任務被加入宏任務隊列,第二次循環時調用。

Promise 屬于微任務,但是 Promise 初始化中代碼會立即進行。所以會立即輸出 6 和 8;

Promise 初始化后的回調放入微任務隊列

第二個 setTimeout 也屬于宏任務源,回調函數的任務放入宏任務隊列,第三次事件循環時調用

繼續調用棧,輸出 10, 沒毛病

第一次事件循環的宏任務執行完畢,執行余下的所有微任務,所以輸出 7,

第二次事件循環,發現有宏任務,即第一個 setTimeout 的回調,輸出 2,調用 Promise 構建函數的調用棧,直接執行,所以輸出3 和 5

第一個 setTimeout 的 promise 回調放入微任務隊列。

第二次事件循環的宏任務調用執行完,執行剛才前一步 Promise 創建的微任務,輸出 4,第二次循環執行完畢。

進入第 3 次事件循環,只有一個宏任務,即第二個 SetTimeout,所以輸出 9;

關于事件循環宏任務和微任務的執行過程:

首先兩個類型的任務都是逐個執行

微任務會前下一個渲染或垃圾回收前全部執行完

一次事件循環中先只執行一個宏任務,在下一次事件循環前執行完所有的微任務,包括新創建的微任務。

五、web worker

盡管 HTML5 新標準加入了 web worker 的多線程技術,但是 web worker 只能用于計算,并且 JS 的多線程 worker 無法操作 DOM, 不然就無法控制頁面是在被誰操作的了。

主線程傳給子線程的數據是通過拷貝復制,同樣子線程傳給主線程的數據也是通過拷貝復制,而不是共享同一個內存空間。

以上說明,JS 不存在線程同步,所以還是可以把 JS 看做單線程模型,把 web worker 當做 JS 的一種回調機制。

總結

事件循環是 JS 和 Nodejs 事件調用機制的核心,保證了頁面可以有序無阻塞的進行。

事件循環的主要邏輯是先執行調用棧,直到清空調用棧只剩下全局上下文。

然后 JS 檢查宏任務隊列,如果有任務則取出一個進行調用,進行頁面渲染和垃圾回收。

同時將所有的微任務源派發的任務加入微任務事件隊列,最后執行余下的所有微任務。微任務執行后完,進行頁面渲染和垃圾回收后進行下一輪事件循環。

歡迎關注我的個人公眾號“謝南波”,專注分享原創文章。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99366.html

相關文章

  • JS專題數組去重

    摘要:將元素作為對象的鍵,默認鍵對應的值為如果對象中沒有這個鍵,則將這個元素放入結果數組中去。 前言 數組去重在日常開發中的使用頻率還是較高的,也是網上隨便一抓一大把的話題,所以,我寫這篇文章目的在于歸納和總結,既然很多人都在提的數組去重,自己到底了解多少呢。又或者是如果自己在開發中遇到了去重的需求,自己能想到更好的解決方案嗎。 這次我們來理一理怎么做數組去重才能做得最合適,既要考慮兼容性,...

    only_do 評論0 收藏0
  • JS異步編程callback

    摘要:而異步則是相反,調用在發出之后,這個調用就直接返回了,所以沒有返回結果而是在調用發出后,被調用者通過狀態通知來通知調用者,或通過回調函數處理這個調用。總結回調函數是異步編程中的基石,但同時也存在很多問題,不太適合人類自然語言的線性思維習慣。 為什么 JS 是單線程? 眾所周知,Javascript 語言的執行環境是單線程(single thread)。 所謂單線程,就是指一次只能完成一...

    superw 評論0 收藏0
  • JavaScript專題系列文章

    摘要:專題系列共計篇,主要研究日常開發中一些功能點的實現,比如防抖節流去重類型判斷拷貝最值扁平柯里遞歸亂序排序等,特點是研究專題之函數組合專題系列第十六篇,講解函數組合,并且使用柯里化和函數組合實現模式需求我們需要寫一個函數,輸入,返回。 JavaScript 專題之從零實現 jQuery 的 extend JavaScritp 專題系列第七篇,講解如何從零實現一個 jQuery 的 ext...

    Maxiye 評論0 收藏0
  • JS專題垃圾回收

    摘要:如果沒有引用指向該對象零引用,對象將被垃圾回收機制回收。經過增量標記改進后,垃圾回收的最大停頓時間可以減少到原來的左右。解除引用的真正作用是讓值脫離執行環境,以便垃圾收集器下次運行時將其回收。 前言 在講 JS 的垃圾回收(Garbage Collection)之前,我們回顧上一篇《JS專題之memoization》,memoization 的原理是以參數作為 key,函數結果作為 v...

    liujs 評論0 收藏0
  • [面試專題]JS異步原理(事件,隊列)

    摘要:全部代碼是一個先執行一個執行過程中遇到等異步操作則創建一個遇到等創建一個這兩個分別被掛起執行棧為空時開始處理完成后處理直到該全部執行完然后繼續主線程調用棧注每一次事件循環,只處理一個。 JS異步原理(事件,隊列) 調用棧 JS執行時會形成調用棧,調用一個函數時,返回地址、參數、本地變量都會被推入棧中,如果當前正在運行的函數中調用另外一個函數,則該函數相關內容也會被推入棧頂.該函數執...

    LeoHsiun 評論0 收藏0

發表評論

0條評論

mengera88

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<