成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Javascript事件循環(huán)機制以及渲染引擎何時渲染UI

cnio / 605人閱讀

摘要:的一大特點就是單線程,而這個線程中擁有唯一的一個事件循環(huán)。事件循環(huán)基本概念代碼的執(zhí)行過程中,除了依靠函數調用棧來搞定函數的執(zhí)行順序外,還依靠任務隊列來搞定另外一些代碼的執(zhí)行。之后全局上下文進入函數調用棧。

JavaScript的一大特點就是單線程,而這個線程中擁有唯一的一個事件循環(huán)。
事件循環(huán)基本概念

JavaScript代碼的執(zhí)行過程中,除了依靠函數調用棧來搞定函數的執(zhí)行順序外,還依靠任務隊列(task queue)來搞定另外一些代碼的執(zhí)行。

一個線程中,事件循環(huán)是唯一的,但是任務隊列可以擁有多個。

任務隊列又分為macro-task(宏任務)與micro-task(微任務),在最新標準中,它們被分別稱為task與jobs。

macro-task大概包括:script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering。

micro-task大概包括: process.nextTick, Promise, Object.observe(已廢棄), MutationObserver(html5新特性)

setTimeout/Promise等我們稱之為任務源。而進入任務隊列的是他們指定的具體執(zhí)行任務。

// setTimeout中的回調函數才是進入任務隊列的任務
setTimeout(function() {
    console.log("xxxx");
})
// 非常多的同學對于setTimeout的理解存在偏差。所以大概說一下誤解:
// setTimeout作為一個任務分發(fā)器,這個函數會立即執(zhí)行,而它所要分發(fā)的任務,也就是它的第一個參數,才是延遲執(zhí)行

來自不同任務源的任務會進入到不同的任務隊列。其中setTimeout與setInterval是同源的。

其中每一個任務的執(zhí)行,無論是macro-task還是micro-task,都是借助函數調用棧來完成。

事件循環(huán)執(zhí)行循序

事件循環(huán)的順序,決定了JavaScript代碼的執(zhí)行順序。它從script(整體代碼)開始第一次循環(huán)。之后全局上下文進入函數調用棧。直到調用棧清空(只剩全局),然后執(zhí)行所有的micro-task。當所有可執(zhí)行的micro-task執(zhí)行完畢之后,本輪循環(huán)結束。下一輪循環(huán)再次從macro-task開始,找到其中一個任務隊列執(zhí)行完畢,然后再執(zhí)行所有的micro-task,這樣一直循環(huán)下去。

當我們在執(zhí)行setTimeout任務中遇到setTimeout時,它仍然會將對應的任務分發(fā)到setTimeout隊列中去,但是該任務就得等到下一輪事件循環(huán)執(zhí)行。

那么整個事件循環(huán)中何時進行ui render呢?
begin
setTimeout(function() {
    // 應該是這里執(zhí)行前開始渲染ui,試試用alert阻塞下。
    alert(" ui 已經渲染完畢了嗎? ");
    console.log("timeout1");
})

new Promise(function(resolve) {
    console.log("promise1");
    for(var i = 0; i < 1000; i++) {
        i == 99 && resolve();
    }
    console.log("promise2");
}).then(function() {
    console.log("then1");
    alert(" ui 開始渲染 ");
})

console.log("global1");

div.innerHTML = "end";

上述代碼中修改了div的內容,那么在執(zhí)行那句js代碼之后渲染引擎開始修改div的內容呢?

根據HTML Standard,一輪事件循環(huán)執(zhí)行結束之后,下輪事件循環(huán)執(zhí)行之前開始進行UI render。即:macro-task任務執(zhí)行完畢,接著執(zhí)行完所有的micro-task任務后,此時本輪循環(huán)結束,開始執(zhí)行UI render。UI render完畢之后接著下一輪循環(huán)。

在chrome瀏覽器中執(zhí)行以上代碼,控制臺先輸出promise1,promise2,global1,then1(micro-task任務輸出),彈出"ui 開始渲染"警告框,點擊確定之后,頁面中的"begin"變?yōu)?end",再彈出警告框"ui 已經渲染完畢了嗎?" ,點擊確認之后再輸入timeout1.

再來一個稍微復雜一點的例子
1
begin
// Let"s get hold of those elements
var outer = document.querySelector(".outer");
var inner = document.querySelector(".inner");

var i = 0;

// Let"s listen for attribute changes on the
// outer element
new MutationObserver(function() {
    console.log("mutate");
}).observe(outer, {
    attributes: true
});

// Here"s a click listener…
function onClick() {
    i++;

    if(i === 1) {
        inner.innerHTML = "end";
    }

    console.log("click");

    setTimeout(function() {
        alert("錨點");
        console.log("timeout");
    }, 0);

    Promise.resolve().then(function() {
        console.log("promise");
    });


    outer.setAttribute("data-random", Math.random());
}

// …which we"ll attach to both elements
inner.addEventListener("click", onClick);
outer.addEventListener("click", onClick);

當我們點擊 inner div 時程序依次的執(zhí)行順序是:

onclick 入 JS stack

打印出 click

將 timeout 壓入到 macrotask

將 promise 壓入到 microtask

修改 outer 屬性 data-random

將 mutate 壓入到 microtask,

onclick 出 JS stack

此時,由于用戶點擊事件onclick產生的macrotask執(zhí)行完畢,JS stack 清空,開始執(zhí)行microtask.

promise 入 JS stack

打印出 promise

promise 出 JS stack

mutate 入 JS stack

打印出 mutate

mutate 出 JS stack

此時,microtask 執(zhí)行完畢,JS stack 清空,但是由于事件冒泡,接著執(zhí)行outer上的onclick事件.

onclick 入 JS stack

打印出 click

將 timeout 壓入到 macrotask

將 promise 壓入到 microtask

修改 outer 屬性 data-random

將 mutate 壓入到 microtask,

onclick 出 JS stack

此時,由于outer上的onclick事件產生的macrotask執(zhí)行完畢,JS stack 清空,開始執(zhí)行microtask.

promise 入 JS stack

打印出 promise

promise 出 JS stack

mutate 入 JS stack

打印出 mutate

mutate 出 JS stack

此時,本輪事件循環(huán)結束,UI 開始 render.

頁面中inner的innerHTML變?yōu)閑nd

此時,UI render 完畢,開始下一輪事件循環(huán).

timeout 入 JS stack

彈出警告 錨點.

打印出 timeout

timeout 出 JS stack

timeout 入 JS stack

彈出警告 錨點.

打印出 timeout

timeout 出 JS stack

到此為止,整個事件執(zhí)行完畢,我們可以看到在彈出警告框之前inner的內容已經改變。

那如果不是用戶點擊事件觸發(fā)onclick,而是js觸發(fā)呢?
inner.addEventListener("click", onClick);
outer.addEventListener("click", onClick);
inner.click();

此時的執(zhí)行順序是:

首先是script(整體代碼)入 JS stack

onclick 入 JS stack

打印出 click

將 timeout 壓入到 macrotask

將 promise 壓入到 microtask

修改 outer 屬性 data-random

將 mutate 壓入到 microtask,

onclick 出 JS stack

此時,inner 的 onclick 已經出 JS stack,但是script(整體代碼)還沒有出 JS stack,還不能執(zhí)行microtask,由于冒泡,接著執(zhí)行 outer 的 onclick.

onclick 入 JS stack

打印出 click

將 timeout 壓入到 macrotask

將 promise 壓入到 microtask

修改 outer 屬性 data-random

接著執(zhí)行的outer.setAttribute("data-random", Math.random());,但是由于上一個mutation microtask還處于等待狀態(tài),不能再添加mutation microtask,所以這里不會將 mutate 壓入到 microtask。接著執(zhí)行:

onclick 出 JS stack

script(整體代碼)出 JS stack

此時,inner.click()執(zhí)行完畢,script(整體代碼)已出 JS stack,JS stack 清空,開始執(zhí)行mircotask.

promise 入 JS stack

打印出 promise

promise 出 JS stack

mutate 入 JS stack

打印出 mutate

mutate 出 JS stack

promise 入 JS stack

打印出 promise

promise 出 JS stack

此時,所有的mircotask執(zhí)行完畢,本輪事件循環(huán)結束,UI 開始 render.

頁面中inner的innerHTML變?yōu)閑nd

此時,UI render 完畢,開始下一輪事件循環(huán).

timeout 入 JS stack

彈出警告 錨點.

打印出 timeout

timeout 出 JS stack

timeout 入 JS stack

彈出警告 錨點.

打印出 timeout

timeout 出 JS stack

到此為止,整個事件執(zhí)行完畢,我們可以看到在彈出警告框之前inner的內容已經改變。

總結:首先執(zhí)行macrotask,當js stack為空時執(zhí)行microtask,接著開始UI render,接著再開始下一輪循環(huán)

參考文獻:
深入核心,詳解事件循環(huán)機制
Tasks, microtasks, queues and schedules

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

轉載請注明本文地址:http://systransis.cn/yun/107342.html

相關文章

  • 關于vue中next和Tick(nextTick)的一點理解

    摘要:為什么叫按照官網的解釋在下次更新循環(huán)結束之后執(zhí)行延遲回調。在修改數據之后立即使用這個方法,獲取更新后的。在下個事件循環(huán)執(zhí)行時確實是最新的了,但是回調并沒有在下個事件循環(huán)執(zhí)行。 前言 在這之前我是沒有怎么看過vue源碼的,但是看了源碼后又產生了一些疑問,如果不看源碼我還真沒有任何疑問的去用nextTick,因為我只知道我想獲取更新后的dom我就在里面寫回調,只管寫準沒錯,有天好奇調試了下...

    mgckid 評論0 收藏0
  • 事件循環(huán)機制和task執(zhí)行順序的一些概括(javascript)

    摘要:而當響應成功了以后,瀏覽器的事件表則會將回調函數添加至事件隊列中等待執(zhí)行。事件循環(huán)器會不停的檢查事件隊列,如果不為空,則取出隊首壓入執(zhí)行棧執(zhí)行。類型的任務目前包括了以及的回調函數。 事件循環(huán)(event loop) : 首先說事件隊列(task queue) 事件隊列是一個存儲著待執(zhí)行任務的隊列,其中的任務嚴格按照時間先后順序執(zhí)行,排在隊頭的任務將會率先執(zhí)行,而排在隊尾的任務會最后執(zhí)行...

    未東興 評論0 收藏0
  • 事件循環(huán)機制

    摘要:事件觸發(fā)線程主要負責將準備好的事件交給引擎線程執(zhí)行。進程瀏覽器渲染進程瀏覽器內核,主要負責頁面的渲染執(zhí)行以及事件的循環(huán)。第二輪循環(huán)結束。 將自己讀到的比較好的文章分享出來,大家互相學習,各位大佬有好的文章也可以留個鏈接互相學習,萬分感謝! 線程與進程 關于線程與進程的關系可以用下面的圖進行說明: showImg(https://segmentfault.com/img/bVbjSZt?...

    Blackjun 評論0 收藏0
  • 事件循環(huán)機制

    摘要:事件觸發(fā)線程主要負責將準備好的事件交給引擎線程執(zhí)行。進程瀏覽器渲染進程瀏覽器內核,主要負責頁面的渲染執(zhí)行以及事件的循環(huán)。第二輪循環(huán)結束。 將自己讀到的比較好的文章分享出來,大家互相學習,各位大佬有好的文章也可以留個鏈接互相學習,萬分感謝! 線程與進程 關于線程與進程的關系可以用下面的圖進行說明: showImg(https://segmentfault.com/img/bVbjSZt?...

    CloudwiseAPM 評論0 收藏0

發(fā)表評論

0條評論

cnio

|高級講師

TA的文章

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