參考文章:
- 阮一峯ES5教程:異步操作 —— 理解異步操作的基本模式和控制流程,瞭解定時任務的實現,Promise的用法
- 前端需要了解的瀏覽器原理(佔坑)
- Inside look at modern web browser (part 1) —— 該系列文章一共4節,詳細介紹了瀏覽器的工作原理。建議先看part1,然後看過參考文章4後,還想要更加深入全面地瞭解瀏覽器再看part2-4。
- 瀏覽器的工作原理:新式網絡瀏覽器幕後揭祕 —— HowBrowsersWork中譯版,看完可瞭解瀏覽器的基本組件和呈現引擎的工作流程
最開始通過看ES5教程瞭解JS異步操作時,理解了JS的單線程模型和事件循環機制,教程中也介紹了實現JS異步操作的方法。但是我還是一頭霧水,爲什麼需要異步操作?怎麼劃分異步操作和同步操作?應該在哪裏進行異步操作?我腦中都是模糊的概念。後來我發現我就是少根筋,我只是需要了解一下異步操作的使用場景和通常的異步任務。
注意:JS層面的異步和操作系統層面的異步(涉及進程線程併發)是有所區別的,建議分開看待,不易混淆。
一、JS的單線程模型和事件循環機制
當我們談論JS層面的異步時,不得不談JS單線程模型和事件循環機制。這是JS異步概念的來源。
JS的單線程模型意味着,在執行JS時只有一個主線程,每個任務必須順序執行。如果當前任務執行時間過長,會導致接下來的所有任務都處於阻塞狀態,進而導致瀏覽器卡死等我們不希望看到的狀況。爲了解決這一問題,事件循環機制(Event Loop)被髮明出來。
事件循環機制中,負責執行JS腳本的單線程我們稱爲主線程,在內存中表現爲一個執行棧,JS只通過主線程執行任務。異步任務被掛起,存儲在堆中,當異步任務準備就緒,它對應的事件便進入任務隊列。主線程首先執行同步任務,然後查看任務隊列是否有就緒的異步任務(或者時間到了的異步任務),調用相應的回調函數執行,直到任務隊列爲空。至此即完成一個事件循環。如下圖所示。
關於JS的事件循環機制更詳細的資料可以參考MDN和ES5教程。
二、異步任務
異步任務指的是被JS引擎放在一邊,不進入主線程而進入任務隊列的任務。有時,什麼任務應被定義爲異步任務,這是由開發者決定的(比如你也可以把Ajax設爲同步任務)。常見的異步任務主要有3類:
- 網絡請求(Ajax)
- 事件觸發(onclick | onchange etc.)
- 定時函數(setTimeout | setInterval)
這些異步操作是由瀏覽器內核呈現引擎進行執行的。針對不同類型的異步任務,通常會開不同的線程執行(如事件觸發線程、定時器觸發線程(Timer)、異步http請求線程)。
三、微任務(microtask)與宏任務(macrotask)
處理異步任務的方法不同,JS中的任務還可分爲:微任務和宏任務。微任務指的是在本輪事件循環中執行的異步任務,宏任務則指同步任務和下輪事件循環之後執行的異步任務。
微任務:Promise
宏任務:同步任務,定時任務(setTimeout | setInterval)
四、如何實現異步
我根據不同的異步場景,對相應的解決方案進行總結。
網絡請求
在JS腳本中進行網絡請求,即AJAX請求。
在瀏覽器呈現頁面的過程中,當HTML解析器碰到<script>的時候,HTML文檔的解析工作會暫停,先執行腳本。也可以將腳本標記爲defer,在HTML文檔解析完成後再執行。HTML5中增加了一個選項可標爲異步,由其他線程解析和執行。(overview of the parsing model)
解析到</script>時,會根據腳本中對DOM結構的操作調用樹構造階段,重新調整DOM樹。因此<script>腳本通常放在HTML文檔下方最後處理,避免反覆修改或者產生我們不想要的效果。
1、XMLHttpRequest和AJAX
2、Promise和AJAX
Promise對象使得能夠通過鏈式寫法處理回調。它可以處理任何異步操作而不僅限於AJAX。
3、jQuery和AJAX
使用jQuery的相關對象處理AJAX不需要考慮瀏覽器問題,使得代碼更簡化。jQuery可以看做一個JavaScript更上一層的封裝,AJAX的本質還是不變的。
jQuery在全局對象jQuery(即$)中綁定了ajax()函數來處理AJAX請求。jQuery中還存在jqXHR對象用鏈式寫法處理回調。
4、Window.fetch
Fetch API 提供了請求資源的接口(包括網絡資源),並返回一個Promise對象。
5、Ajxos等庫
總結:Ajax本質不變,實現它的方式多種多樣。
事件觸發
定時任務
定時任務通常使用setTimeout()、setInterval() 兩個方法實現。
(施工中)