摘要:主線程要明確的一點是,主線程跟執行棧是不同概念,主線程規定現在執行執行棧中的哪個事件。主線程循環即主線程會不停的從執行棧中讀取事件,會執行完所有棧中的同步代碼。以上參考資料詳解中的事件循環機制中的事件循環運行機制詳解再談
前言
大家都知道js是單線程的腳本語言,在同一時間,只能做同一件事,為了協調事件、用戶交互、腳本、UI渲染和網絡處理等行為,防止主線程阻塞,Event Loop方案應運而生...
個人博客了解一下:obkoro1.com為什么js是單線程?
js作為主要運行在瀏覽器的腳本語言,js主要用途之一是操作DOM。
在js高程中舉過一個栗子,如果js同時有兩個線程,同時對同一個dom進行操作,這時瀏覽器應該聽哪個線程的,如何判斷優先級?
為了避免這種問題,js必須是一門單線程語言,并且在未來這個特點也不會改變。
執行棧與任務隊列因為js是單線程語言,當遇到異步任務(如ajax操作等)時,不可能一直等待異步完成,再繼續往下執行,在這期間瀏覽器是空閑狀態,顯而易見這會導致巨大的資源浪費。
執行棧當執行某個函數、用戶點擊一次鼠標,Ajax完成,一個圖片加載完成等事件發生時,只要指定過回調函數,這些事件發生時就會進入執行棧隊列中,等待主線程讀取,遵循先進先出原則。
主線程要明確的一點是,主線程跟執行棧是不同概念,主線程規定現在執行執行棧中的哪個事件。
主線程循環:即主線程會不停的從執行棧中讀取事件,會執行完所有棧中的同步代碼。
當遇到一個異步事件后,并不會一直等待異步事件返回結果,而是會將這個事件掛在與執行棧不同的隊列中,我們稱之為任務隊列(Task Queue)。
當主線程將執行棧中所有的代碼執行完之后,主線程將會去查看任務隊列是否有任務。如果有,那么主線程會依次執行那些任務隊列中的回調函數。
不太理解的話,可以運行一下下面的代碼,或者點擊一下這個demo
結果是當a、b、c函數都執行完成之后,三個setTimeout才會依次執行。
let a = () => { setTimeout(() => { console.log("任務隊列函數1") }, 0) for (let i = 0; i < 5000; i++) { console.log("a的for循環") } console.log("a事件執行完") } let b = () => { setTimeout(() => { console.log("任務隊列函數2") }, 0) for (let i = 0; i < 5000; i++) { console.log("b的for循環") } console.log("b事件執行完") } let c = () => { setTimeout(() => { console.log("任務隊列函數3") }, 0) for (let i = 0; i < 5000; i++) { console.log("c的for循環") } console.log("c事件執行完") } a(); b(); c(); // 當a、b、c函數都執行完成之后,三個setTimeout才會依次執行js 異步執行的運行機制。
所有任務都在主線程上執行,形成一個執行棧。
主線程之外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務隊列"。那些對應的異步任務,結束等待狀態,進入執行棧并開始執行。
主線程不斷重復上面的第三步。
宏任務與微任務:異步任務分為 宏任務(macrotask) 與 微任務 (microtask),不同的API注冊的任務會依次進入自身對應的隊列中,然后等待 Event Loop 將它們依次壓入執行棧中執行。
宏任務(macrotask)::
script(整體代碼)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 環境)
微任務(microtask):
Promise、 MutaionObserver、process.nextTick(Node.js環境)
Event Loop(事件循環):Event Loop(事件循環)中,每一次循環稱為 tick, 每一次tick的任務如下:
執行棧選擇最先進入隊列的宏任務(通常是script整體代碼),如果有則執行
檢查是否存在 Microtask,如果存在則不停的執行,直至清空 microtask 隊列
更新render(每一次事件循環,瀏覽器都可能會去更新渲染)
重復以上步驟
宏任務 > 所有微任務 > 宏任務,如下圖所示:
從上圖我們可以看出:
將所有任務看成兩個隊列:執行隊列與事件隊列。
執行隊列是同步的,事件隊列是異步的,宏任務放入事件列表,微任務放入執行隊列之后,事件隊列之前。
當執行完同步代碼之后,就會執行位于執行列表之后的微任務,然后再執行事件列表中的宏任務
上面提到的demo結果可以這么理解:先執行script宏任務,執行完了之后,再執行其他兩個定時器宏任務。
面試題實踐下面這個題,很多人都應該看過/遇到過,重新來看會不會覺得清晰很多:
// 執行順序問題,考察頻率挺高的,先自己想答案** setTimeout(function () { console.log(1); }); new Promise(function(resolve,reject){ console.log(2) resolve(3) }).then(function(val){ console.log(val); }) console.log(4);
根據本文的解析,我們可以得到:
先執行script同步代碼
先執行new Promise中的console.log(2),then后面的不執行屬于微任務
然后執行console.log(4)
執行完script宏任務后,執行微任務,console.log(3),沒有其他微任務了。
執行另一個宏任務,定時器,console.log(1)。
根據本文的內容,可以很輕松,且有理有據的猜出寫出正確答案:2,4,3,1.
類似上文的面試題還有很多,實則都大同小異,只要掌握了事件循環的機制,這些問題都會變得很簡單。
文章如有不正確的地方歡迎各位路過的大佬鞭策!希望大家看完可以有所收獲,喜歡的話,趕緊點波訂閱關注/喜歡。
個人blog and 掘金個人主頁,如需轉載,請放上原文鏈接并署名。碼字不易,感謝支持!
如果喜歡本文的話,歡迎關注我的訂閱號,漫漫技術路,期待未來共同學習成長。
以上2018.6.16
參考資料:詳解JavaScript中的Event Loop(事件循環)機制
JavaScript中的事件循環 Event Loop
JavaScript 運行機制詳解:再談Event Loop
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95513.html
摘要:設置和清除定時器直接引用忍者秘籍中的圖片注意定時器的時間間隔設為,也會有幾毫秒的延遲。以上參考資料忍者秘籍第章馴服線程和定時器 showImg(https://segmentfault.com/img/remote/1460000015353524?w=1024&h=681); 前言 前段時間剛看完《JS忍者秘籍》,雖說是15年出版的,有些東西是過時了,但像對原型鏈、閉包、正則、定時器...
摘要:圖片轉引自的演講和兩個定時器中回調的執行邏輯便是典型的機制。異步編程關于異步編程我的理解是,在執行環境所提供的異步機制之上,在應用編碼層面上實現整體流程控制的異步風格。 問題背景 在一次開發任務中,需要實現如下一個餅狀圖動畫,基于canvas進行繪圖,但由于對于JS運行環境中異步機制的不了解,所以遇到了一個棘手的問題,始終無法解決,之后在與同事交流之后才恍然大悟。問題的根節在于經典的J...
摘要:瀏覽器與的異同,以及部分機制有人對部分迷惑,本身構造函數是同步的,是異步。瀏覽器的的已全部分析完成,過程中引用阮一峰博客,知乎,部分文章內容,侵刪。 瀏覽器與NodeJS的EventLoop異同,以及部分機制 PS:有人對promise部分迷惑,Promise本身構造函數是同步的,.then是異步。---- 2018/7/6 22:35修改 javascript 是一門單線程的腳本...
摘要:了解事件循環機制有助于理解的執行過程,同時這也是面試常見題。那么這個回調函數將在何時由誰執行呢已知是瀏覽器環境提供的,因此瀏覽器將對它進行處理,瀏覽器會在本次事件完成,即計時結束后,將回調函數加入循環隊列中,然后等待被加入執行棧執行。 如果有人問JavaScript是什么,也許你會說它是一個單線程、非阻塞、異步、解釋型的腳本語言。那么作為一個單線程語言,它是怎么實現非阻塞、異步的?這就...
閱讀 2676·2021-11-16 11:53
閱讀 2737·2021-07-26 23:38
閱讀 2073·2019-08-30 15:55
閱讀 1751·2019-08-30 13:21
閱讀 3650·2019-08-29 17:26
閱讀 3306·2019-08-29 13:20
閱讀 875·2019-08-29 12:20
閱讀 3192·2019-08-26 10:21