国产精品爱久久久久久久小说,女人扒开腿让男人桶到爽 ,亚洲欧美国产双大乳头,国产成人精品综合久久久久,国产精品制服丝袜无码,免费无码精品黄av电影,黑色丝袜无码中中文字幕,乱熟女高潮一区二区在线

            JavaScript中的Event Loop(事件循環(huán))機(jī)制

            2020-5-25    seo達(dá)人

            事件循環(huán)

            JavaScript是單線程,非阻塞的

            瀏覽器的事件循環(huán)


            執(zhí)行棧和事件隊(duì)列

            宏任務(wù)和微任務(wù)

            node環(huán)境下的事件循環(huán)


            和瀏覽器環(huán)境有何不同

            事件循環(huán)模型

            宏任務(wù)和微任務(wù)

            經(jīng)典題目分析

            1. JavaScript是單線程,非阻塞的

            單線程:


            JavaScript的主要用途是與用戶互動(dòng),以及操作DOM。如果它是多線程的會(huì)有很多復(fù)雜的問(wèn)題要處理,比如有兩個(gè)線程同時(shí)操作DOM,一個(gè)線程刪除了當(dāng)前的DOM節(jié)點(diǎn),一個(gè)線程是要操作當(dāng)前的DOM階段,最后以哪個(gè)線程的操作為準(zhǔn)?為了避免這種,所以JS是單線程的。即使H5提出了web worker標(biāo)準(zhǔn),它有很多限制,受主線程控制,是主線程的子線程。


            非阻塞:通過(guò) event loop 實(shí)現(xiàn)。


            2. 瀏覽器的事件循環(huán)

            執(zhí)行棧和事件隊(duì)列

            為了更好地理解Event Loop,請(qǐng)看下圖(轉(zhuǎn)引自Philip Roberts的演講 《Help, I'm stuck in an event-loop》)

            Help, I'm stuck in an event-loop


            執(zhí)行棧: 同步代碼的執(zhí)行,按照順序添加到執(zhí)行棧中


            function a() {

               b();

               console.log('a');

            }

            function b() {

               console.log('b')

            }

            a();

            我們可以通過(guò)使用 Loupe(Loupe是一種可視化工具,可以幫助您了解JavaScript的調(diào)用堆棧/事件循環(huán)/回調(diào)隊(duì)列如何相互影響)工具來(lái)了解上面代碼的執(zhí)行情況。


            調(diào)用情況


            執(zhí)行函數(shù) a()先入棧

            a()中先執(zhí)行函數(shù) b() 函數(shù)b() 入棧

            執(zhí)行函數(shù)b(), console.log('b') 入棧

            輸出 b, console.log('b')出棧

            函數(shù)b() 執(zhí)行完成,出棧

            console.log('a') 入棧,執(zhí)行,輸出 a, 出棧

            函數(shù)a 執(zhí)行完成,出棧。

            事件隊(duì)列: 異步代碼的執(zhí)行,遇到異步事件不會(huì)等待它返回結(jié)果,而是將這個(gè)事件掛起,繼續(xù)執(zhí)行執(zhí)行棧中的其他任務(wù)。當(dāng)異步事件返回結(jié)果,將它放到事件隊(duì)列中,被放入事件隊(duì)列不會(huì)立刻執(zhí)行起回調(diào),而是等待當(dāng)前執(zhí)行棧中所有任務(wù)都執(zhí)行完畢,主線程空閑狀態(tài),主線程會(huì)去查找事件隊(duì)列中是否有任務(wù),如果有,則取出排在第一位的事件,并把這個(gè)事件對(duì)應(yīng)的回調(diào)放到執(zhí)行棧中,然后執(zhí)行其中的同步代碼。


            我們?cè)偕厦娲a的基礎(chǔ)上添加異步事件,


            function a() {

               b();

               console.log('a');

            }

            function b() {

               console.log('b')

               setTimeout(function() {

                   console.log('c');

               }, 2000)

            }

            a();

            此時(shí)的執(zhí)行過(guò)程如下

            img


            我們同時(shí)再加上點(diǎn)擊事件看一下運(yùn)行的過(guò)程


            $.on('button', 'click', function onClick() {

               setTimeout(function timer() {

                   console.log('You clicked the button!');    

               }, 2000);

            });


            console.log("Hi!");


            setTimeout(function timeout() {

               console.log("Click the button!");

            }, 5000);


            console.log("Welcome to loupe.");

            img


            簡(jiǎn)單用下面的圖進(jìn)行一下總結(jié)


            執(zhí)行棧和事件隊(duì)列


            宏任務(wù)和微任務(wù)

            為什么要引入微任務(wù),只有一種類型的任務(wù)不行么?


            頁(yè)面渲染事件,各種IO的完成事件等隨時(shí)被添加到任務(wù)隊(duì)列中,一直會(huì)保持先進(jìn)先出的原則執(zhí)行,我們不能準(zhǔn)確地控制這些事件被添加到任務(wù)隊(duì)列中的位置。但是這個(gè)時(shí)候突然有高優(yōu)先級(jí)的任務(wù)需要盡快執(zhí)行,那么一種類型的任務(wù)就不合適了,所以引入了微任務(wù)隊(duì)列。


            不同的異步任務(wù)被分為:宏任務(wù)和微任務(wù)

            宏任務(wù):


            script(整體代碼)

            setTimeout()

            setInterval()

            postMessage

            I/O

            UI交互事件

            微任務(wù):


            new Promise().then(回調(diào))

            MutationObserver(html5 新特性)

            運(yùn)行機(jī)制

            異步任務(wù)的返回結(jié)果會(huì)被放到一個(gè)任務(wù)隊(duì)列中,根據(jù)異步事件的類型,這個(gè)事件實(shí)際上會(huì)被放到對(duì)應(yīng)的宏任務(wù)和微任務(wù)隊(duì)列中去。


            在當(dāng)前執(zhí)行棧為空時(shí),主線程會(huì)查看微任務(wù)隊(duì)列是否有事件存在


            存在,依次執(zhí)行隊(duì)列中的事件對(duì)應(yīng)的回調(diào),直到微任務(wù)隊(duì)列為空,然后去宏任務(wù)隊(duì)列中取出最前面的事件,把當(dāng)前的回調(diào)加到當(dāng)前指向棧。

            如果不存在,那么再去宏任務(wù)隊(duì)列中取出一個(gè)事件并把對(duì)應(yīng)的回到加入當(dāng)前執(zhí)行棧;

            當(dāng)前執(zhí)行棧執(zhí)行完畢后時(shí)會(huì)立刻處理所有微任務(wù)隊(duì)列中的事件,然后再去宏任務(wù)隊(duì)列中取出一個(gè)事件。同一次事件循環(huán)中,微任務(wù)永遠(yuǎn)在宏任務(wù)之前執(zhí)行。


            在事件循環(huán)中,每進(jìn)行一次循環(huán)操作稱為 tick,每一次 tick 的任務(wù)處理模型是比較復(fù)雜的,但關(guān)鍵步驟如下:


            執(zhí)行一個(gè)宏任務(wù)(棧中沒(méi)有就從事件隊(duì)列中獲取)

            執(zhí)行過(guò)程中如果遇到微任務(wù),就將它添加到微任務(wù)的任務(wù)隊(duì)列中

            宏任務(wù)執(zhí)行完畢后,立即執(zhí)行當(dāng)前微任務(wù)隊(duì)列中的所有微任務(wù)(依次執(zhí)行)

            當(dāng)前宏任務(wù)執(zhí)行完畢,開(kāi)始檢查渲染,然后GUI線程接管渲染

            渲染完畢后,JS線程繼續(xù)接管,開(kāi)始下一個(gè)宏任務(wù)(從事件隊(duì)列中獲取)

            簡(jiǎn)單總結(jié)一下執(zhí)行的順序:

            執(zhí)行宏任務(wù),然后執(zhí)行該宏任務(wù)產(chǎn)生的微任務(wù),若微任務(wù)在執(zhí)行過(guò)程中產(chǎn)生了新的微任務(wù),則繼續(xù)執(zhí)行微任務(wù),微任務(wù)執(zhí)行完畢后,再回到宏任務(wù)中進(jìn)行下一輪循環(huán)。


            宏任務(wù)和微任務(wù)


            深入理解js事件循環(huán)機(jī)制(瀏覽器篇) 這邊文章中有個(gè)特別形象的動(dòng)畫(huà),大家可以看著理解一下。


            console.log('start')


            setTimeout(function() {

             console.log('setTimeout')

            }, 0)


            Promise.resolve().then(function() {

             console.log('promise1')

            }).then(function() {

             console.log('promise2')

            })


            console.log('end')

            瀏覽器事件循環(huán)


            全局代碼壓入執(zhí)行棧執(zhí)行,輸出 start

            setTimeout壓入 macrotask隊(duì)列,promise.then 回調(diào)放入 microtask隊(duì)列,最后執(zhí)行 console.log('end'),輸出 end

            調(diào)用棧中的代碼執(zhí)行完成(全局代碼屬于宏任務(wù)),接下來(lái)開(kāi)始執(zhí)行微任務(wù)隊(duì)列中的代碼,執(zhí)行promise回調(diào),輸出 promise1, promise回調(diào)函數(shù)默認(rèn)返回 undefined, promise狀態(tài)變成 fulfilled ,觸發(fā)接下來(lái)的 then回調(diào),繼續(xù)壓入 microtask隊(duì)列,此時(shí)產(chǎn)生了新的微任務(wù),會(huì)接著把當(dāng)前的微任務(wù)隊(duì)列執(zhí)行完,此時(shí)執(zhí)行第二個(gè) promise.then回調(diào),輸出 promise2

            此時(shí),microtask隊(duì)列 已清空,接下來(lái)會(huì)會(huì)執(zhí)行 UI渲染工作(如果有的話),然后開(kāi)始下一輪 event loop, 執(zhí)行 setTimeout的回調(diào),輸出 setTimeout

            最后的執(zhí)行結(jié)果如下


            start

            end

            promise1

            promise2

            setTimeout

            node環(huán)境下的事件循環(huán)

            和瀏覽器環(huán)境有何不同

            表現(xiàn)出的狀態(tài)與瀏覽器大致相同。不同的是 node 中有一套自己的模型。node 中事件循環(huán)的實(shí)現(xiàn)依賴 libuv 引擎。Node的事件循環(huán)存在幾個(gè)階段。


            如果是node10及其之前版本,microtask會(huì)在事件循環(huán)的各個(gè)階段之間執(zhí)行,也就是一個(gè)階段執(zhí)行完畢,就會(huì)去執(zhí)行 microtask隊(duì)列中的任務(wù)。


            node版本更新到11之后,Event Loop運(yùn)行原理發(fā)生了變化,一旦執(zhí)行一個(gè)階段里的一個(gè)宏任務(wù)(setTimeout,setInterval和setImmediate)就立刻執(zhí)行微任務(wù)隊(duì)列,跟瀏覽器趨于一致。下面例子中的代碼是按照的去進(jìn)行分析的。


            事件循環(huán)模型

            ┌───────────────────────┐

            ┌─>│        timers         │

            │  └──────────┬────────────┘

            │  ┌──────────┴────────────┐

            │  │     I/O callbacks     │

            │  └──────────┬────────────┘

            │  ┌──────────┴────────────┐

            │  │     idle, prepare     │

            │  └──────────┬────────────┘      ┌───────────────┐

            │  ┌──────────┴────────────┐      │   incoming:   │

            │  │         poll          │<──connections───     │

            │  └──────────┬────────────┘      │   data, etc.  │

            │  ┌──────────┴────────────┐      └───────────────┘

            │  │        check          │

            │  └──────────┬────────────┘

            │  ┌──────────┴────────────┐

            └──┤    close callbacks    │

              └───────────────────────┘

            事件循環(huán)各階段詳解

            node中事件循環(huán)的順序


            外部輸入數(shù)據(jù) --> 輪詢階段(poll) --> 檢查階段(check) --> 關(guān)閉事件回調(diào)階段(close callback) --> 定時(shí)器檢查階段(timer) --> I/O 事件回調(diào)階段(I/O callbacks) --> 閑置階段(idle, prepare) --> 輪詢階段...


            這些階段大致的功能如下:


            定時(shí)器檢測(cè)階段(timers): 這個(gè)階段執(zhí)行定時(shí)器隊(duì)列中的回調(diào)如 setTimeout() 和 setInterval()。

            I/O事件回調(diào)階段(I/O callbacks): 這個(gè)階段執(zhí)行幾乎所有的回調(diào)。但是不包括close事件,定時(shí)器和setImmediate()的回調(diào)。

            閑置階段(idle, prepare): 這個(gè)階段僅在內(nèi)部使用,可以不必理會(huì)

            輪詢階段(poll): 等待新的I/O事件,node在一些特殊情況下會(huì)阻塞在這里。

            檢查階段(check): setImmediate()的回調(diào)會(huì)在這個(gè)階段執(zhí)行。

            關(guān)閉事件回調(diào)階段(close callbacks): 例如socket.on('close', ...)這種close事件的回調(diào)

            poll:

            這個(gè)階段是輪詢時(shí)間,用于等待還未返回的 I/O 事件,比如服務(wù)器的回應(yīng)、用戶移動(dòng)鼠標(biāo)等等。

            這個(gè)階段的時(shí)間會(huì)比較長(zhǎng)。如果沒(méi)有其他異步任務(wù)要處理(比如到期的定時(shí)器),會(huì)一直停留在這個(gè)階段,等待 I/O 請(qǐng)求返回結(jié)果。

            check:

            該階段執(zhí)行setImmediate()的回調(diào)函數(shù)。


            close:

            該階段執(zhí)行關(guān)閉請(qǐng)求的回調(diào)函數(shù),比如socket.on('close', ...)。


            timer階段:

            這個(gè)是定時(shí)器階段,處理setTimeout()和setInterval()的回調(diào)函數(shù)。進(jìn)入這個(gè)階段后,主線程會(huì)檢查一下當(dāng)前時(shí)間,是否滿足定時(shí)器的條件。如果滿足就執(zhí)行回調(diào)函數(shù),否則就離開(kāi)這個(gè)階段。


            I/O callback階段:

            除了以下的回調(diào)函數(shù),其他都在這個(gè)階段執(zhí)行:


            setTimeout()和setInterval()的回調(diào)函數(shù)

            setImmediate()的回調(diào)函數(shù)

            用于關(guān)閉請(qǐng)求的回調(diào)函數(shù),比如socket.on('close', ...)

            宏任務(wù)和微任務(wù)

            宏任務(wù):


            setImmediate

            setTimeout

            setInterval

            script(整體代碼)

            I/O 操作等。

            微任務(wù):


            process.nextTick

            new Promise().then(回調(diào))

            Promise.nextTick, setTimeout, setImmediate的使用場(chǎng)景和區(qū)別

            Promise.nextTick

            process.nextTick 是一個(gè)獨(dú)立于 eventLoop 的任務(wù)隊(duì)列。

            在每一個(gè) eventLoop 階段完成后會(huì)去檢查 nextTick 隊(duì)列,如果里面有任務(wù),會(huì)讓這部分任務(wù)優(yōu)先于微任務(wù)執(zhí)行。

            是所有異步任務(wù)中最快執(zhí)行的。


            setTimeout:

            setTimeout()方法是定義一個(gè)回調(diào),并且希望這個(gè)回調(diào)在我們所指定的時(shí)間間隔后第一時(shí)間去執(zhí)行。


            setImmediate:

            setImmediate()方法從意義上將是立刻執(zhí)行的意思,但是實(shí)際上它卻是在一個(gè)固定的階段才會(huì)執(zhí)行回調(diào),即poll階段之后。


            經(jīng)典題目分析

            一. 下面代碼輸出什么

            async function async1() {

               console.log('async1 start');

               await async2();

               console.log('async1 end');

            }

            async function async2() {

               console.log('async2');

            }

            console.log('script start');

            setTimeout(function() {

               console.log('setTimeout');

            }, 0)

            async1();

            new Promise(function(resolve) {

               console.log('promise1');

               resolve();

            }).then(function() {

               console.log('promise2');

            });

            console.log('script end');

            先執(zhí)行宏任務(wù)(當(dāng)前代碼塊也算是宏任務(wù)),然后執(zhí)行當(dāng)前宏任務(wù)產(chǎn)生的微任務(wù),然后接著執(zhí)行宏任務(wù)


            從上往下執(zhí)行代碼,先執(zhí)行同步代碼,輸出 script start

            遇到setTimeout,現(xiàn)把 setTimeout 的代碼放到宏任務(wù)隊(duì)列中

            執(zhí)行 async1(),輸出 async1 start, 然后執(zhí)行 async2(), 輸出 async2,把 async2() 后面的代碼 console.log('async1 end')放到微任務(wù)隊(duì)列中

            接著往下執(zhí)行,輸出 promise1,把 .then()放到微任務(wù)隊(duì)列中;注意Promise本身是同步的立即執(zhí)行函數(shù),.then是異步執(zhí)行函數(shù)

            接著往下執(zhí)行, 輸出 script end。同步代碼(同時(shí)也是宏任務(wù))執(zhí)行完成,接下來(lái)開(kāi)始執(zhí)行剛才放到微任務(wù)中的代碼

            依次執(zhí)行微任務(wù)中的代碼,依次輸出 async1 end、 promise2, 微任務(wù)中的代碼執(zhí)行完成后,開(kāi)始執(zhí)行宏任務(wù)中的代碼,輸出 setTimeout

            最后的執(zhí)行結(jié)果如下


            script start

            async1 start

            async2

            promise1

            script end

            async1 end

            promise2

            setTimeout

            二. 下面代碼輸出什么

            console.log('start');

            setTimeout(() => {

               console.log('children2');

               Promise.resolve().then(() => {

                   console.log('children3');

               })

            }, 0);


            new Promise(function(resolve, reject) {

               console.log('children4');

               setTimeout(function() {

                   console.log('children5');

                   resolve('children6')

               }, 0)

            }).then((res) => {

               console.log('children7');

               setTimeout(() => {

                   console.log(res);

               }, 0)

            })

            這道題跟上面題目不同之處在于,執(zhí)行代碼會(huì)產(chǎn)生很多個(gè)宏任務(wù),每個(gè)宏任務(wù)中又會(huì)產(chǎn)生微任務(wù)


            從上往下執(zhí)行代碼,先執(zhí)行同步代碼,輸出 start

            遇到setTimeout,先把 setTimeout 的代碼放到宏任務(wù)隊(duì)列①中

            接著往下執(zhí)行,輸出 children4, 遇到setTimeout,先把 setTimeout 的代碼放到宏任務(wù)隊(duì)列②中,此時(shí).then并不會(huì)被放到微任務(wù)隊(duì)列中,因?yàn)?resolve是放到 setTimeout中執(zhí)行的

            代碼執(zhí)行完成之后,會(huì)查找微任務(wù)隊(duì)列中的事件,發(fā)現(xiàn)并沒(méi)有,于是開(kāi)始執(zhí)行宏任務(wù)①,即第一個(gè) setTimeout, 輸出 children2,此時(shí),會(huì)把 Promise.resolve().then放到微任務(wù)隊(duì)列中。

            宏任務(wù)①中的代碼執(zhí)行完成后,會(huì)查找微任務(wù)隊(duì)列,于是輸出 children3;然后開(kāi)始執(zhí)行宏任務(wù)②,即第二個(gè) setTimeout,輸出 children5,此時(shí)將.then放到微任務(wù)隊(duì)列中。

            宏任務(wù)②中的代碼執(zhí)行完成后,會(huì)查找微任務(wù)隊(duì)列,于是輸出 children7,遇到 setTimeout,放到宏任務(wù)隊(duì)列中。此時(shí)微任務(wù)執(zhí)行完成,開(kāi)始執(zhí)行宏任務(wù),輸出 children6;

            最后的執(zhí)行結(jié)果如下


            start

            children4

            children2

            children3

            children5

            children7

            children6

            三. 下面代碼輸出什么

            const p = function() {

               return new Promise((resolve, reject) => {

                   const p1 = new Promise((resolve, reject) => {

                       setTimeout(() => {

                           resolve(1)

                       }, 0)

                       resolve(2)

                   })

                   p1.then((res) => {

                       console.log(res);

                   })

                   console.log(3);

                   resolve(4);

               })

            }



            p().then((res) => {

               console.log(res);

            })

            console.log('end');

            執(zhí)行代碼,Promise本身是同步的立即執(zhí)行函數(shù),.then是異步執(zhí)行函數(shù)。遇到setTimeout,先把其放入宏任務(wù)隊(duì)列中,遇到p1.then會(huì)先放到微任務(wù)隊(duì)列中,接著往下執(zhí)行,輸出 3

            遇到 p().then 會(huì)先放到微任務(wù)隊(duì)列中,接著往下執(zhí)行,輸出 end

            同步代碼塊執(zhí)行完成后,開(kāi)始執(zhí)行微任務(wù)隊(duì)列中的任務(wù),首先執(zhí)行 p1.then,輸出 2, 接著執(zhí)行p().then, 輸出 4

            微任務(wù)執(zhí)行完成后,開(kāi)始執(zhí)行宏任務(wù),setTimeout, resolve(1),但是此時(shí) p1.then已經(jīng)執(zhí)行完成,此時(shí) 1不會(huì)輸出。

            最后的執(zhí)行結(jié)果如下


            3

            end

            2

            4

            你可以將上述代碼中的 resolve(2)注釋掉, 此時(shí) 1才會(huì)輸出,輸出結(jié)果為 3 end 4 1。


            const p = function() {

               return new Promise((resolve, reject) => {

                   const p1 = new Promise((resolve, reject) => {

                       setTimeout(() => {

                           resolve(1)

                       }, 0)

                   })

                   p1.then((res) => {

                       console.log(res);

                   })

                   console.log(3);

                   resolve(4);

               })

            }



            p().then((res) => {

               console.log(res);

            })

            console.log('end');

            3

            end

            4

            1

            最后強(qiáng)烈推薦幾個(gè)非常好的講解 event loop 的視頻:


            What the heck is the event loop anyway? | Philip Roberts | JSConf EU

            Jake Archibald: In The Loop - JSConf.Asia

            日歷

            鏈接

            個(gè)人資料

            存檔

            主站蜘蛛池模板: 国精品无码一区二区三区在线a片| 操到喷水| 韩国三级中文字幕hd久久精品| 天干夜啦天干天干国产免费| 亚洲色图14p| 日本高清在线播放一区二区三区| 久久精品视频18| 中国毛片在线观看| 老司机深夜18禁污污网站| 久久不见久久见www免费视频| 麻豆国产原创| 乱人伦人妻中文字幕不卡| 国产精品黄页免费高清在线观看| 有码中文av无码中文av| 五月天小说网| 亚洲熟妇av日韩熟妇在线| 久久综合亚洲色hezyo社区| 色婷亚洲五月| 免费无码av污污污在线观看| 成人久久精品人妻一区二区三区| www.av.cn| 少妇厨房愉情理9仑片视频下载| 国产亚洲精品成人aa片| 久久99精品久久久久久不卡| 在线观看麻豆国产成人av在线播放| 男人添女人囗交做爰视频| 亚洲线精品一区二区三八戒| 丰满大肥婆肥奶大屁股| 国产在线成人一区二区三区| 无遮挡在线观看| 久久久精品94久久精品| 无码av一区二区三区不卡| 99久视频| 成人免费无码大片a毛片| 亚洲国产日韩一区三区| 亚洲色成人网站www永久四虎| 少妇高清一区二区免费看| 日韩精品少妇| 成人网免费视频| 国产成人无码精品一区在线观看| 韩国三色电费2024免费吗怎么看 |