過年啦,過年啦!lotoze在這裏爲你們送上新年祝福:
祝@全部人,在新的一年裏個個身體健康,萬事如意!
祝@武漢人民,跨過一切困難險阻,打敗一切疫情!
祝@爸媽爺爺奶奶,幸福美滿,健康長壽!
祝@愛人,一直永遠開心下去,我將永遠愛你!
祝@我,可以讓我身邊的人在2020年更好!javascript
【JS深淵】系列是我用來對前端原生javascript語言相關技術進行的深度探究或者筆記記錄,我我的是一個很愛去挖層的這麼一我的,但不幸的是覺悟的太晚,但我不會放棄。決定開始寫博客目的有兩個,一個是有儀式性的去持續提升本身的技術能力,另外一個就是但願向上再向上。不過本身能力也是有限的,若是寫的很差或者出現什麼紕漏又或者錯誤的地方,脫褲式歡迎你們的建議、指正。沒錯看着我,對,盯着你的屏幕,別眨眼,請記住個人這句話:
您的反饋是就是我持續進步的動力。
好了,收!biu~html
在當咱們寫完js代碼執行的時候,其內部的執行機制很是複雜,但主要會經歷三個過程:語法分析、預編譯、真正執行。這其中,在真正執行這一過程當中是最龐雜的。此篇幅就是爲真正執行這一過程的分析而誕生。 若是對前兩個過程有疑惑請點擊此處【JS深淵】幹它!必定要完全弄懂javascript執行機制(一)前端
我記得我當時研究js的執行機制,是由於一個問題:java
js是一個單線程語言,是否表明着參與js執行過程的線程只有一個?git
答案是否認的。在我回答爲何以前咱們須要去了解一些知識點。這是我爲了解決這個問題去百度的線程知識點。github
以上是摘自百度百科,由於我再總結也沒有百度總結的好啊。可是上邊的這些是給非人類看的。也就是看不懂的話——俗稱「廢話」。promise
對於咱們來講,無需把全部都記住,由於知識是學不完的,要只學本身須要的。那麼回到一開始的問題上來,剛纔說答案是否認的,緣由是什麼?瀏覽器
咱們一般所說的js基本是依賴於瀏覽器的。瀏覽器也是咱們前端打交道最多的。咱們說瀏覽器是計算機系統上的一個軟件。進程是程序的實體,是系統資源分配的最小單位。也就是說當你啓動瀏覽器時,其實就是啓動了一個瀏覽器進程。進程是線程的容器,一個進程下能夠併發包含多個線程。瀏覽器能夠作的事情遠遠不止一件事,除了能夠解釋執行js代碼,還能夠渲染頁面、異步執行、計時器/定時器、事件監聽等。因此這確定不只僅是一個線程就能夠作到的。因此參與js執行過程的線程不止一個。bash
既然js執行過程參與的線程不止一個,那麼參與的線程有哪些呢?數據結構
參與的有4個線程,其中JS引擎線程只負責執行js,其餘三個線程進行配合。值得一提的是,GUI渲染線程負責渲染html元素,可是在js引擎線程執行腳本的時候,GUI渲染引擎是被掛起的。
總結一下,都是javascript線程在運行js腳本,其餘三個線程都是進行在知足條件是將執行函數推入事件隊列中,等待JS引擎主線程執行。
爲了更好的理解執行過程,須要引用個例子(英文原版),這個例子很是經典,建議英文基礎好的閱讀,很是不錯的文章。
console.log("script start");
setTimeout(function () {
console.log("setTimeout");
}, 0)
Promise.reslove().then(function() {
console.log("promise1");
}).then(function() {
console.log("promise2");
})
console.log("script end");
複製代碼
這裏直接按照執行過程劃分代碼,這裏只簡單描述一下過程。
console.log("script start");
console.log("script end");
複製代碼
setTimeout(function () {
console.log("setTimeout");
}, 0)
複製代碼
Promise.resolve().then(function () {
console.log("promise1");
}).then(function () {
console.log("promise2");
})
複製代碼
在js引擎的執行過程當中,進入執行階段,代碼的執行順序以下:
宏任務(同步任務)---> 微任務 ---> 宏任務(異步任務)
複製代碼
輸出結果爲:
"script start"
"script end"
"promise1"
"promise2"
"setTimeout"
複製代碼
在ES6或Node的環境中,JS的任務分爲兩種,分別是宏任務(macro-task)和微任務(micro-tsk),在最新的ECMAScript中,微任務稱爲jobs,宏任務稱爲task,他們的執行順序如上。不少人對上面的執行順序分析不是很理解,那麼咱們接下來繼續對上面例子進行詳細分析。
宏任務(macro-task)可分爲同步任務和異步任務。
理解宏任務中的同步任務和異步任務的執行順序,那麼就至關於理解了JS異步任務執行機制——事件輪詢(Event Loop)
事件輪詢能夠理解爲由三個部分組成,分別是:
任務隊列(task queue)就是以隊列的數據結構對事件任務進行管控,特色是先進先出,後進後出。
在JS引擎主線程執行過程當中:這裏直接引用一張著名的圖片(參考自Philip Roberts的演講《Help, I’m stuck in an event-loop》),幫助咱們理解:
若是仍是不理解,那麼咱們再拿上面的例子進行詳細的分析,該例子中宏任務的代碼部分是:
console.log("script start");
setTimeout(function () {
console.log("setTimeout");
}, 0)
console.log("script end");
複製代碼
執行過程以下:
以上就是JS引擎執行宏任務的整個過程。
理解了宏任務中的執行過程後,跟lotoze放飛一下,拓展性思考幾個問題:
setTimeout和setInterval都是異步任務的定時器,須要添加到任務隊列中等待JS引擎主線程空閒時間讀取任務隊列執行,那麼使用setTimeout實現setInterval,會有區別嗎?
答案是有區別的。不妨思考一下:
lotoze我在不久之前對於seTimeout和setInterval的認知還停留在setTimeout只執行一次,setInterval能夠按照時間間隔循環不停的執行。可是如今卻有了不少新的認識。
高頻率觸發事件(例如滾動監聽、input輸出)觸發頻率太高會影響頁面性能,甚至形成頁面卡頓,咱們是否能夠利用計時器的原理來進行優化呢?
是能夠的。咱們能夠利用setTimeout實現計時器的原理,對高頻率的事件監聽進行優化,實現點在於多個事件合併成一個,這就是防抖和節流。這裏我會在後面的文章中去詳細講解一下實現方式,這裏就很少說啦。
微任務是在ES6和Node環境中出現的一個任務類型,若是不考慮ES6和Node環境的話,咱們只須要理解宏任務中事件輪詢的執行過程就足夠了,可是到了ES6和Node環境,就須要理解摻雜了微任務的執行順序了。
微任務(micro-task0)的API主要有:Promise、process.nextTick。
這裏直接引用一張流程圖幫助咱們理解一下:
在宏任務中執行的任務有兩種,分別是 同步任務和 異步任務,由於異步任務會在知足觸發條件時纔會推動任務隊列(task queue),而後等待主線程上的任務執行完畢,再讀取任務隊列中的任務事件,最後推動主線程執行,因此這裏將異步任務即任務隊列看作是新的宏任務,執行過程如上圖所示:這就是加入微任務後的詳細事件輪詢,若是尚未理解,那麼再次對一開始的例子作一個全面的分析以及補充:
console.log("script start");
setTimeout(function () {
console.log("setTimeout");
}, 0)
Promise.reslove().then(function() {
console.log("promise1");
}).then(function() {
console.log("promise2");
})
console.log("script end");
複製代碼
執行過程:
最後的輸出結果是:
"script start"
"script end"
"promise1"
"promise2"
"setTimeout"
複製代碼
怎麼樣?你是否懂了?😁😁
參考:
lotoze | 【原創】
着重說明:
裏面一些表情圖片並不是原創,只是爲了讀者讀起來不是那麼枯燥乏味。但若是原做者以爲有侵犯版權的意思,請使用下方聯繫方式與我聯繫,爲了尊重原創做者的辛苦創做,我將及時處理!
固然,沒事也能夠聯繫啦😘😘歡迎交流!
寫做不易,
若是您還以爲湊合,就給個贊!
若是以爲確實以爲: 「老傢伙,有你的啊!」就加個關注!
若是文章有任何的錯誤,脫褲式歡迎你們來進行批評指正!
每個鼓勵都是lotoze我持續拋頭顱,撒雞血的創做動力!
每個批評反饋也都是lotoze我持續成長的臺階!