Evevt Loop、任務隊列、定時器等

上週五,一個朋友發給我一道面試題,代碼以下:html

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

朋友說在控制檯跑完的結果是依次打印出 1 2 5 3 4,可是不清楚爲何,但願我幫他分析下。面試


額,我以爲setTimeout裏的那個打印,是在下次事件循環時纔會執行的,因此打印結果應該是 1 5 2 3 4 。。。。。瀏覽器

然而,結果並不是如此。說明我還處於朦朧狀態啊,因而乎,開始去網上找博客。多線程

如下內容均是在博客中看到的內容,加上一點點本身的理解,至因而否客觀正確,這個另說哈,時間會檢驗一切的。dom


JS主要用來操做DOM,若是是多線程,會比較混亂,因此是單線程的。

同步:主線程上執行的任務。

異步:進入任務隊列 的任務,只有等主線程上的任務執行完了,任務隊列中的任務纔會進入主線程執行。


瀏覽器中的事件循環--Event Loop(我盜的圖 哈哈)

主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)。異步

從上圖看到(下面這段也是抄的😁):函數

  1. 主線程運行的時候產生堆(heap)和棧(stack)
  2. 執行棧中的代碼調用各類外部API,它們在"任務隊列"中加入各類事件(click,load,done)
  3. 只要執行棧中的代碼執行完畢,主線程就會去讀取"任務隊列",將隊列中的事件放到執行棧中依次執行。
  4. 主線程繼續執行,當再調用外部API時又加入到任務隊列中,等主線程執行完畢又會接着將任務隊列中的事件放到主線程中。
  5. 上面整個過程是循環不斷的。

不知道從哪引用的

除了setTimeout和setInterval這兩個方法,Node.js還提供了另外兩個與"任務隊列"有關的方法:process.nextTick和setImmediate。

process.nextTick方法能夠在當前"執行棧"的尾部----下一次Event Loop(主線程讀取"任務隊列")以前----觸發回調函數。也就是說,它指定的任務老是發生在全部異步任務以前。 
setImmediate方法則是在當前"任務隊列"的尾部添加事件,也就是說,它指定的任務老是在下一次Event Loop時執行,這與setTimeout(fn, 0)很像。

結論

其實還有不少東西沒有好好整理出來,下面只是大概記錄下,詳細的內容,查看下面的博文連接哈oop

若是在原來的setTimeout console外面包了function。執行結果是1 5 3 4 2。post

首先,1 5 是同步任務,因此主線程中執行,setTimeout屬於宏任務,添加到任務隊列中,Promise是異步的,屬於微任務,在同步任務執行完畢後執行,而且它也是鏈式的,因此它的回調函數會依次被添加到微任務中,微任務會在任務隊列中的每個task執行完後執行線程


總之,setTimeout(fn,0)的含義是,指定某個任務在主線程最先可得的空閒時間執行,也就是說,儘量早得執行。它在"任務隊列"的尾部添加一個事件,所以要等到同步任務和"任務隊列"現有的事件都處理完,纔會獲得執行。

HTML5標準規定了setTimeout()的第二個參數的最小值(最短間隔),不得低於4毫秒,若是低於這個值,就會自動增長。在此以前,老版本的瀏覽器都將最短間隔設爲10毫秒。另外,對於那些DOM的變更(尤爲是涉及頁面從新渲染的部分),一般不會當即執行,而是每16毫秒執行一次。這時使用requestAnimationFrame()的效果要好於setTimeout()。

須要注意的是,setTimeout()只是將事件插入了"任務隊列",必須等到當前代碼(執行棧)執行完,主線程纔會去執行它指定的回調函數。要是當前代碼耗時很長,有可能要等好久,因此並無辦法保證,回調函數必定會在setTimeout()指定的時間執行。

But,還有個問題沒搞明白:
setTimeout( function(){ console.log(2) }, 0 );

setTimeout( console.log(2), 0 );
有啥子區別呢?執行結果不同呢

參考:

你真的瞭解Event Loop(事件環)嗎?
JavaScript 運行機制詳解:再談Event Loop

相關文章
相關標籤/搜索