剛接觸js的時候,對於es6的promise、async、await簡直怕的要死,甚至有段時間很是懼怕promise這個詞,隨着後面慢慢的接觸,以爲這個東西並不是那麼難理解,主要仍是須要弄懂js的一些基礎知識。那麼接下來,跟上個人思路,一塊兒完全弄懂promise、async、await。前端
若是要弄懂promise,就必須弄懂什麼是異步、什麼是同步,這篇文章主要是講一下什麼是同步、什麼是異步。node
任何新語言的出現確定是與他當時的需求有關係的,js全稱是Javascript,誕生於1995年(跟我同歲)。
最初他的誕生就是爲了表單提交的時候作提示用的,在js問世以前,全部的表單都必須提交到服務端才能校驗必填項,
好比你想申請一個qq號,各類信息填了一大堆,提交完才知道,你手機號少輸入了一位從新輸入,
那確定砸電腦的心都有了,這個時候,js出生了,由於是跟用戶作實時交互的,因此最先叫livescript,
當時爲了蹭蹭Java的熱度,上戶口的時候就改爲了Javascript,一不當心長大了能夠跟Java分庭抗禮了。
複製代碼
js從誕生之初就是單線程,那爲何是單線程呢?
爲了讓咱們這些菜雞更容易入門?固然不是。
js主要的用途就是操做DOM,以及與用戶的交互,這就決定了他只能是單線程,
好比你這個線程建立了一個DOM,那個線程給刪除了,這時候瀏覽器應該以哪一個爲準,
因此這個應該永遠不會變,你前端發展的能造火箭了,js確定也是單線程的。
複製代碼
你能夠理解爲同一個時間,你只能幹一件事。今天下班早,你想給女友打個電話,女友可能跟其餘小夥伴一塊兒吃飯呢,
因爲手機靜音,因此聽不到,你就一直打,一直打,啥都沒幹,把時間都浪費了,這就叫同步。由於js是單線程的嘛,因此js從小就是同步的。
來一段代碼:
function second() {
console.log('second')
}
function first(){
console.log('first')
second()
console.log('Last')
}
first()
這個很簡單,執行打印結果:
first、second、last
複製代碼
那麼js執行這段代碼,到底發生了什麼呢?這裏面又有一個‘調用棧’的概念es6
是否是一聽到什麼堆棧就懼怕,別慌,沒那麼複雜,你能夠理解爲一個廁所,你們去上廁所,可是!不是先進先出,而是後進先出。用調用棧的概念,解釋一下上面代碼的執行順序:promise
當執行此代碼時,將建立一個全局執行上下文並將其推到調用堆棧的頂部;// 這個不過重要,下面是重點
first()函數先上,如今他在頂部;
而後打印‘first’,而後執行完了,這個時候這個console.log會自動彈走,就是這個console.log雖然是後進來的,可是他先走了;
如今first函數仍然在頂部,他下面還有second函數,因此不會彈走;
執行second()函數,這時候second函數在頂部;
打印‘second’,而後執行完了,彈走這個console.log,這時候second在頂部;
這個時候second函數的事兒都幹完了,他也彈走了,這時候first函數在頂部;
瀏覽器會問,first你還有事嗎,first說我還有一個,執行打印‘last’
複製代碼
電話沒打通,你就給女友發了個短信,洗澡去了,你回家了告訴我,(等我洗完了)再打給你,這就是異步。
後來爲了提升效率,把瀏覽器的多個內核都用起來,HTML5提出Web Worker標準,
容許JavaScript腳本建立多個線程,可是子線程徹底受主線程控制,且不得操做DOM。
因此這並無影響js單線程的本質,js仍是每次只能幹一件事,只不過把洗澡提早了而已。
來段代碼:
const getList = () => {
setTimeout(() => {
console.log('我執行了!');
}, 2000);
};
console.log('Hello World');
getList();
console.log('哈哈哈');
執行順序是:
Hello World、哈哈哈、我執行了!(兩秒之後執行最後一個)
複製代碼
這段代碼執行,又發生了什麼呢?這個地方又有一個‘消息隊列’的概念,不慌!瀏覽器
剛纔咱們說了,同步的時候,瀏覽器會維護一個‘執行棧’,除了執行棧,在開啓多線程的時候,瀏覽器還會維護一個消息列表,除了主線程,其他的都是副線程,這些副線程合起來就叫消息列表。 咱們用消息列表的概念分析一下上面的代碼:微信
按照執行順序console.log('Hello World')先執行,瀏覽器一看,中央軍(主線程)!你先過;
而後是getlist函數執行,瀏覽器看到setTimeout,你是八L(副線程)!你先靠邊等着;
而後是console.log('哈哈哈')執行,中央軍(主線程)!你也過;
而後瀏覽器問,還有中央軍嗎?沒了,八L開始過!
複製代碼
setTimeout(function() {
console.log('我是定時器!');
})
new Promise(function(resolve) {
console.log('我是promise!');
resolve();
}).then(function() {
console.log('我是then!');
})
console.log('我是主線程!');
執行順序:
我是promise!
我是主線程!
我是then!
我是定時器!
複製代碼
爲何promise.then比定時器先執行呢?這個裏面又涉及了一個‘事件輪詢’的概念。多線程
上面咱們說了,瀏覽器爲了提高效率,爲js開啓了一個不太同樣的多線程,由於js不能同時執行嘛,那副線程(注意是副線程裏面哈)裏面誰執行,這個選擇的過程,就能夠理解爲事件輪詢。咱們先用事件輪詢的順序分析一下上面的代碼,再來上概念:異步
promise函數確定首先執行,他是主線程嘛,打印‘我是promise’;
而後繼續走主線程,打印‘我是主線程’;
而後主線程走完了,開始走消息列表;
(宏任務和微任務一會再講)
這個時候會先執行promise.then,由於他是微任務,裏面的‘我是then!’
消息列表裏面在上面的是定時器,可是定時器是宏任務,優先級比較低,因此會日後排;
複製代碼
**宏任務(Macrotasks):**js同步執行的代碼塊,setTimeout、setInterval、XMLHttprequest、setImmediate、I/O、UI rendering等。
**微任務(Microtasks):**promise、process.nextTick(node環境)、Object.observe, MutationObserver等。
微任務比宏任務要牛逼一點
瀏覽器執行的順序:
(1)執行主代碼塊,這個主代碼塊也是宏任務
(2)若遇到Promise,把then以後的內容放進微任務隊列
(3)遇到setTimeout,把他放到宏任務裏面
(4)一次宏任務執行完成,檢查微任務隊列有無任務
(5)有的話執行全部微任務
(6)執行完畢後,開始下一次宏任務。
複製代碼
可能寫了那麼多尚未涉及到promise和async、await,可是前面的同步、異步是基礎,只有搞懂了同步異步究竟是怎麼回事,纔能有基礎去學promise,下一章就來聊聊怎麼讓同步變異步,異步變同步,哪些地方用到!async
在這兒感謝掘金大神的文章,爲了表示尊重,掛上地址!函數