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

資訊專欄INFORMATION COLUMN

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

cnio / 578人閱讀

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

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

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

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

任務(wù)隊(duì)列又分為macro-task(宏任務(wù))與micro-task(微任務(wù)),在最新標(biāo)準(zhǔn)中,它們被分別稱為task與jobs。

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

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

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

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

來自不同任務(wù)源的任務(wù)會進(jìn)入到不同的任務(wù)隊(duì)列。其中setTimeout與setInterval是同源的。

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

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

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

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

那么整個事件循環(huán)中何時進(jìn)行ui render呢?
begin
setTimeout(function() {
    // 應(yīng)該是這里執(zhí)行前開始渲染ui,試試用alert阻塞下。
    alert(" ui 已經(jīng)渲染完畢了嗎? ");
    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的內(nèi)容,那么在執(zhí)行那句js代碼之后渲染引擎開始修改div的內(nèi)容呢?

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

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

再來一個稍微復(fù)雜一點(diǎn)的例子
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("錨點(diǎn)");
        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);

當(dāng)我們點(diǎn)擊 inner div 時程序依次的執(zhí)行順序是:

onclick 入 JS stack

打印出 click

將 timeout 壓入到 macrotask

將 promise 壓入到 microtask

修改 outer 屬性 data-random

將 mutate 壓入到 microtask,

onclick 出 JS stack

此時,由于用戶點(diǎn)擊事件onclick產(chǎn)生的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事件產(chǎn)生的macrotask執(zhí)行完畢,JS stack 清空,開始執(zhí)行microtask.

promise 入 JS stack

打印出 promise

promise 出 JS stack

mutate 入 JS stack

打印出 mutate

mutate 出 JS stack

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

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

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

timeout 入 JS stack

彈出警告 錨點(diǎn).

打印出 timeout

timeout 出 JS stack

timeout 入 JS stack

彈出警告 錨點(diǎn).

打印出 timeout

timeout 出 JS stack

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

那如果不是用戶點(diǎn)擊事件觸發(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 已經(jīng)出 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)結(jié)束,UI 開始 render.

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

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

timeout 入 JS stack

彈出警告 錨點(diǎn).

打印出 timeout

timeout 出 JS stack

timeout 入 JS stack

彈出警告 錨點(diǎn).

打印出 timeout

timeout 出 JS stack

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

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

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

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

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/107342.html

相關(guān)文章

  • 關(guān)于vue中next和Tick(nextTick)的一點(diǎn)理解

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

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

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

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

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

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

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

    CloudwiseAPM 評論0 收藏0

發(fā)表評論

0條評論

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