最近在檢討,不少知識都是隻會用,不理解底層的知識。因此在開發過程當中遇到一些奇怪的比較難解決的bug,在思考的時候就會收到限制。因此,在這裏一點一點補充基礎知識吧。前端
好啦,下面進入正題面試
技術的出現,都跟現實世界裏的場景密切相關。一樣的,咱們就結合現實場景,來回答這三個問題。ajax
(1)js爲何是單線程的?
js是一種腳本語言,腳本語言是爲了縮短傳統的編寫-編譯-連接-運行過程而建立的計算機編程語言,腳本語言不須要編譯,能夠直接用,由解釋器來負責解釋。
js最初被設計用在瀏覽器中,那麼想象一下,若是瀏覽器中的js是多線程的。。。
場景描述:
如今有兩個進程:process1和process2,因爲是多線程的js,因此它們對同一個DOM同時進行操做process1刪除了該DOM,而process2編輯了該DOM,同時下達了兩個矛盾的命令,你這讓瀏覽器怎麼執行呢?
這樣一想,是否是就理解了js爲何被設計成單線程了吧~~~編程
(2)js爲何是異步的?
場景描述:
若是js不存在異步,只能自上而下執行,若是上一行執行時間很長,好比說沒有網了,那麼下面的代碼就會被阻塞,對於用戶來講,阻塞就意味着「卡死」,這樣致使用戶體驗不好。因爲這個「缺陷」,致使JavaScript的全部網絡操做,瀏覽器事件,都必須是異步執行。
因此,js是存在異步執行的,好比setTimeout、setInterval、ajax、promisepromise
(3)單線程是怎麼實現異步的?
場景描述:
經過Event Loop(事件循環),因此說,理解了Event Loop機制,也就理解了js的執行機制啦。瀏覽器
舉個栗子:觀察下它的執行機制網絡
console.log(1) setTimeout(function(){ console.log(2) },0); console.log(3)
毫無疑問:運行結果是1 3 2多線程
也就是說:setTimeout裏的函數並無當即執行,而是延遲了一段時間,知足必定條件後纔去執行的,咱們叫作異步代碼。異步
因此這裏咱們首先知道了js裏的一種分類方式,就是將任務分爲:同步任務和異步任務。編程語言
按照這種分類方式:js的執行機制是
以上三步循環執行,這就是Event Loop
因此上面的栗子,你是否能夠描述它的執行順序了呢?
console.log(1) //是同步任務,放進主進程裏 setTimeout(function(){ //是異步任務,放進event table,0s以後被推入event queue裏 console.log(2) },0); console.log(3) //是同步任務,放進主進程裏 //當一、3在瀏覽器控制檯被打印後,主線程去event queue中查看是否有可執行的函數,執行setTimeout裏的函數。
因此,上面關於Event Loop就是我對js執行機制的理解,是否是很簡單呢。。。。
慢着!看看下面的這段代碼
又一個栗子:
setTimeout(function(){ console.log("定時器開始啦~~~"); }) new Promise(function(resolve){ console.log("立刻執行for循環啦"); for(var i=0;i<10000;i++){ i==99&&resolve(); } }).then(function(){ console.log("執行then函數啦") }); console.log("表演完畢!");
有沒有很熟悉,好像面試過程當中常常遇見這種問題呀~~~
如今,咱們按照上面學到的js執行機制去分析一下下
setTimeout(function(){ //是異步任務,被放進event table中 console.log("定時器開始啦~~~"); }) new Promise(function(resolve){ //是同步任務,被放進主進程中,直接執行 console.log("立刻執行for循環啦"); for(var i=0;i<10000;i++){ i==99&&resolve(); } }).then(function(){ //是異步任務,被放進event table中 console.log("執行then函數啦") }); console.log("表演完畢!"); //是同步任務,被放進主進程中,直接執行
運行結果是:【立刻執行for循環啦----表演完畢!----執行then函數啦----定時器開始啦~~~】
那麼,難道是異步任務的執行順序不是先後順序,而是另有規定?事實上,按照同步和異步的劃分方式,並不許確。
因此這裏咱們首先知道了js裏的一種分類方式,就是將任務分爲:同步任務和異步任務。
叨叨了半天,原來都是一些「假大空」的東西,搞什麼搞嘛,哼!!!
準確的劃分方式是:
按照這種分類方式:js的執行機制就是
執行一個宏任務,執行過程當中若是遇到微任務,就將其放在微任務的event queue裏
當前宏任務執行完成後,會查看微任務的event queue,並將裏面的所有微任務依次執行完。
重複以上兩個步驟,結合Event Loop第一課和Event Loop第二課,就是更爲準確的js執行機制了。
嘗試按照剛纔學到的執行機制,去分析第二個栗子:
setTimeout(function(){ console.log("定時器開始啦~~~"); }) new Promise(function(resolve){ console.log("立刻執行for循環啦"); for(var i=0;i<10000;i++){ i==99&&resolve(); } }).then(function(){ console.log("執行then函數啦") }); console.log("表演完畢!"); //首先執行script中的宏任務; //遇到setTimeout,放進宏任務的event queue中; //遇到new Promise直接執行,打印「立刻執行for循環啦」; //遇到then方法,是微任務,被放進微任務的event queue //執行打印「表演完畢!」,本輪宏任務執行完畢; //查看本輪微任務,發現then方法裏的函數,執行打印「執行then函數啦」 //到此,本輪Event Loop所有完成 //下一輪循環裏,先執行一個宏任務,發現宏任務的event queue中有一個setTimeout中的函數,打印「定時器開始啦」
運行結果是:【立刻執行for循環啦----表演完畢!----執行then函數啦----定時器開始啦~~~】
下面這段setTimeout代碼是什麼意思?咱們通常說:「3s後,會執行setTimeout裏的函數」
setTimeout(function(){ console.log("執行了"); },3000)
可是這種說法並不嚴謹,準確的解釋是:
3s後,setTimeout裏的函數會被推入到event queue中,而event queue(事件隊列)裏的任務,只有在主線程空閒時纔會執行。
因此:只有在知足
這兩個條件同時知足,纔會在3s後執行該函數。若是主線程執行內容不少,執行時間超過3s,好比執行了10s,那麼這個函數只有在10s後執行啦
到此,js引擎的執行機制解讀完啦,前端的水很深,慢慢摸着吧感謝文章的提供者:ziwei3749