首頁  >  文章  >  web前端  >  細說JavaScript事件循環機制-第二講

細說JavaScript事件循環機制-第二講

韦小宝
韦小宝原創
2018-03-07 15:26:321773瀏覽

我們繼續來講JavaScript中的事件循環機制,第一個講的JavaScript事件循環機制並沒有講完,我們現在繼續接著講,對JavaScript事件循環機制的繼續來看本篇文章吧!

在上一篇文章裡面我大致介紹了JavaScript的事件循環機制,但最後還留下了一段程式碼和幾個問題。

那我們先從這段程式碼開始看哇

(function test() {
    setTimeout(function() {console.log(4)}, 0);
    new Promise(function executor(resolve) {
        console.log(1);
        for( var i=0 ; i<10000 ; i++ ) {
            i == 9999 && resolve();
        }
        console.log(2);
    }).then(function() {
        console.log(5);
    });
    console.log(3);
})()

在這段程式碼裡面,setTimeout和Promise都被稱之為任務來源,來自不同任務來源的回呼函數會被放進不同的任務佇列裡面。

setTimeout的回呼函數被放進setTimeout的任務佇列之中。而對於Promise,它的回呼函數並不是傳進去的executer函數,而是其非同步執行的then方法裡面的參數,被放進Promise的任務佇列之中。也就是說Promise的第一個參數並不會被放進Promise的任務佇列之中,而會在目前隊列就執行。

其中setTimeout和Promise的任務佇列叫做macro-task(巨集任務),當然如我們所想,還有micro-task(微任務)。

  1. macro-task包含:script(整體程式碼), setTimeout, setInterval, setImmediate, I/O, UI rendering。

  2. micro-task包含:process.nextTick, Promises, Object.observe, MutationObserver

其中上面的setImmediate和process.nextTick是Node .JS裡面的API,瀏覽器裡面並沒有,這裡就當舉例,不必糾結具體是怎麼實現的。

事件循環的順序是從script開始第一次循環,隨後全局上下文進入函數調用棧,碰到macro-task就將其交給處理它的模組處理完之後將回調函數放進macro -task的佇列之中,碰到micro-task也是將其回呼函數放進micro-task的佇列之中。直到函式呼叫堆疊清空只剩下全域執行上下文,然後開始執行所有的micro-task。當所有可執行的micro-task執行完畢之後。迴圈再次執行macro-task中的一個任務佇列,執行完之後再執行所有的micro-task,就這樣一直循環。

分析執行過程

下面分析的想法依照波同學先前所寫的深入核心,詳解事件循環機制中的思路進行分析。

以先前的栗子作為分析的對象,來分析事件循環機制究竟是怎麼執行程式碼的

(function test() {
    setTimeout(function() {console.log(4)}, 0);
    new Promise(function executor(resolve) {
        console.log(1);
        for( var i=0 ; i<10000 ; i++ ) {
            i == 9999 && resolve();
        }
        console.log(2);
    }).then(function() {
        console.log(5);
    });
    console.log(3);
})()

注意下面所有圖中的setTimeout任務隊伍和最後的函數調用堆疊中存放的都是setTimeout的回呼函數,並不是整個setTimeout計時器

細說JavaScript事件循環機制-第二講

1.首先,script任務來源先執行,全域上下文入堆疊。

細說JavaScript事件循環機制-第二講

2.script任務來源的程式碼在執行時遇到setTimeout,作為一個macro-task,將其回呼函數放入自己的佇列之中。

細說JavaScript事件循環機制-第二講

3.script任務來源的程式碼在執行時遇到Promise實例。 Promise建構子中的第一個參數是在目前任務直接執行不會被放入佇列之中,因此此時輸出 1 。

細說JavaScript事件循環機制-第二講

細說JavaScript事件循環機制-第二講

4.在for迴圈裡面遇到resolve函數,函數入堆疊執行後出棧,此時Promise的狀態變成Fulfilled。程式碼接著執行遇到console.log(2),輸出2。

細說JavaScript事件循環機制-第二講

5.接著執行,程式碼遇到then方法,其回呼函數作為micro-task入棧,進入Promise的任務佇列之中。

細說JavaScript事件循環機制-第二講

6.程式碼接著執行,此時遇到console.log(3),輸出3。

細說JavaScript事件循環機制-第二講細說JavaScript事件循環機制-第二講

7.输出3之后第一个宏任务script的代码执行完毕,这时候开始开始执行所有在队列之中的micro-task。then的回调函数入栈执行完毕之后出栈,这时候输出5

細說JavaScript事件循環機制-第二講

8.这时候所有的micro-task执行完毕,第一轮循环结束。第二轮循环从setTimeout的任务队列开始,setTimeout的回调函数入栈执行完毕之后出栈,此时输出4。

总结

总的来说就是:

1、不同的任务会放进不同的任务队列之中。

2、先执行macro-task,等到函数调用栈清空之后再执行所有在队列之中的micro-task。

3、等到所有micro-task执行完之后再从macro-task中的一个任务队列开始执行,就这样一直循环。

4、当有多个macro-task(micro-task)队列时,事件循环的顺序是按上文macro-task(micro-task)的分类中书写的顺序执行的。

测试

说到这里,我们应该都明白了,下面是一个复杂的代码段(改自深入核心,详解事件循环机制),里面有混杂着的micro-task和macro-task,自己画图试试流程哇,然后再用node执行看看输出的顺序是否一致。

console.log(&#39;golb1&#39;);
setImmediate(function() {
    console.log(&#39;immediate1&#39;);
    process.nextTick(function() {
        console.log(&#39;immediate1_nextTick&#39;);
    })
    new Promise(function(resolve) {
        console.log(&#39;immediate1_promise&#39;);
        resolve();
    }).then(function() {
        console.log(&#39;immediate1_then&#39;)
    })
})
setTimeout(function() {
    console.log(&#39;timeout1&#39;);
    process.nextTick(function() {
        console.log(&#39;timeout1_nextTick&#39;);
    })
    new Promise(function(resolve) {
        console.log(&#39;timeout1_promise&#39;);
        resolve();
    }).then(function() {
        console.log(&#39;timeout1_then&#39;)
    })
    setTimeout(function() {
    	console.log(&#39;timeout1_timeout1&#39;);
    process.nextTick(function() {
        console.log(&#39;timeout1_timeout1_nextTick&#39;);
    })
    setImmediate(function() {
    	console.log(&#39;timeout1_setImmediate1&#39;);
    })
    });
})
new Promise(function(resolve) {
    console.log(&#39;glob1_promise&#39;);
    resolve();
}).then(function() {
    console.log(&#39;glob1_then&#39;)
})
process.nextTick(function() {
    console.log(&#39;glob1_nextTick&#39;);
})

讲到这里我们的细说JavaScript事件循环机制也就正式讲完了,看不懂了两篇结合起来看看,练练即可!

先看看我吧:

细说JavaScript事件循环机制-第一讲

以上是細說JavaScript事件循環機制-第二講的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn